MacOS和windows开机自启动

linux上的开机自启动很简单,通过systemd就能搞定。对于macos和windows的开机自启动则没有记录过,这里记录下。

MacOS开机自启动

Macos提供三种开机自启动的方式,详情可以看这里三种方式配置Mac OS X的启动项。这是一篇12年的老文章了。

这里挑选一种和linux上的systemd很像的方式,使用launchd来进行开机自启动。和systemd一样,launchd也是MacOS上的第一个进程,并且提供和systemctl很类似的launchctl工具。

plist文件

使用Launchd设置开机自启动,仅仅需要编写一个plist文件并将其放到~/Library/LaunchAgents/。以下是一个应用开机自启的plist文件。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
        <dict>
                <key>Label</key>
                <string>com.arloor.sslocal</string>
                <!-- 退出后是否重启 -->
                <key>KeepAlive</key>
                <false />
                <!-- 加载后立即启动,即开机自启。如果设置为false,则bootstrap后还需要kickstart才会启动 -->
                <key>RunAtLoad</key>
                <true />
                <key>WorkingDirectory</key>
                <string>/tmp</string>
                <key>EnvironmentVariables</key>
                <dict>
                        <key>aPATH</key>
                        <string>/bin:/usr/bin:/usr/local/bin</string>
                </dict>
                <key>ProgramArguments</key>
                <array>
                        <string>/Users/bytedance/.cargo/bin/sslocal</string>
                        <string>--local-addr</string>
                        <string>localhost:2080</string>
                        <string>-k</string>
                        <string>xxxxxxxx</string>
                        <string>-m</string>
                        <string>aes-256-gcm</string>
                        <string>-s</string>
                        <string>host:port</string>
                        <string>-v</string>
                </array>
                <!-- 软资源限制 -->
                <key>SoftResourceLimits</key>
                <dict>
                        <key>NumberOfFiles</key>
                        <integer>65536</integer>
                </dict>
                <!-- 硬资源限制 -->
                <key>HardResourceLimits</key>
                <dict>
                        <key>NumberOfFiles</key>
                        <integer>65536</integer>
                </dict>
                <!-- 标准输出路径 -->
                <key>StandardOutPath</key>
                <string>/tmp/sslocal.log</string>
                <!-- 标准错误路径 -->
                <key>StandardErrorPath</key>
                <string>/tmp/sslocal.log</string>
        </dict>
</plist>

如果需要其他字段来自定义能力,可以参考 man launchd.plist,或者看launchd.info

启动和停止

如果想实现类似systemctl restart xx的能力,可以使用下面的脚本:

#! /bin/bash

# 使用while循环读取参数
while [ $# -gt 0 ]; do
    if [ "$1" == "stop" ]; then
        stop=1
    elif [ "$1" != "start" ]; then
        service_name=$1
    fi
    shift # 移除第一个参数
done
[ "$service_name" == "" ] && {
    service_name="com.arloor.sslocal"
}
[ "$stop" == "1" ] && {
    echo "stop and disable [${service_name}]"
} || {
    echo "start and enable [${service_name}]"
}

get_cur_pid() {
    launchctl list | grep ${service_name} | awk '{print $1}'
}

old_pid=$(get_cur_pid)
if [ "$old_pid" != "" ]; then
    echo 关闭老进程 $old_pid
    launchctl bootout gui/$(id -u)/${service_name}
    launchctl disable gui/$(id -u)/${service_name}
fi
if [ "$stop" != "1" ]; then
    launchctl enable gui/$(id -u)/${service_name}
    launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/${service_name}.plist
    pid=$(get_cur_pid)
    if [ "$pid" != "" ]; then
        echo 新进程 $pid
    else
        echo 启动失败
    fi
fi

把这个脚本命名成 systemctl,那你就可以:

systemctl start com.arloor.sslocal
systemctl stop com.arloor.sslocal

service是否被disable的db文件地址如下。MacOS不会自动删除db文件中无效的service,这导致执行launchctl print-disabled gui/$(id -u)时会看到一些无效的service。如果想手动删除这些无效的service,需要先在恢复模式关闭安全模式,然后才能通过vim修改。

/private/var/db/com.apple.xpc.launchd/disabled.$(id -u).plist 

老命令

unload和load是老旧的launchctl命令,man launchctl能看到,官方推荐我们使用 bootstrap | bootout | enable | disable

  • unload -w 等同于 bootout + disable,停止进程并禁用开机自启动。
  • load -w 等同于 enable + bootstrap,启动进程并设置开机自启动。
  • bootstrapbootout 只有在service是enable的状态下才有效。所以下面的脚本中,bootout在disable之前,bootstrap后enable之后。
  • bootstrap 需要使用plist的路径,而不是service-name
  • launchctl kickstart -p 用于打印当前进程的pid
#! /bin/sh
service_name="com.arloor.sslocal"
get_cur_pid() {
    launchctl list | grep ${service_name} | awk '{print $1}'
}
old_pid=$(get_cur_pid)
if [ "$old_pid" != "" ]; then
    echo 关闭老进程 $old_pid
    launchctl unload -w ~/Library/LaunchAgents/${service_name}.plist
fi

if [ "$1" != "stop" ]; then
    sleep 1
    launchctl load -w ~/Library/LaunchAgents/${service_name}.plist
    pid=$(get_cur_pid)
    if [ "$pid" != "" ]; then
        echo 新进程 $pid
    else
        echo 启动失败
    fi
fi

全局资源限制

unix系统都限制了可打开文件数,上面的plist修改了单个进程的文件描述符数量限制。如何修改全局资源限制呢?

  1. 新建/Library/LaunchDaemons/limit.maxfiles.plist文件,写入
<?xml version="1.0" encoding="UTF-8"?>  
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"  
         "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">  
   <dict>
     <key>Label</key>
     <string>limit.maxfiles</string>
     <key>ProgramArguments</key>
     <array>
       <string>launchctl</string>
       <string>limit</string>
       <string>maxfiles</string>
       <string>64000</string>
       <string>524288</string>
     </array>
     <key>RunAtLoad</key>
     <true/>
     <key>ServiceIPC</key>
     <false/>
   </dict>
 </plist>
  1. 修改文件权限
 sudo chown root:wheel /Library/LaunchDaemons/limit.maxfiles.plist
 sudo chmod 644 /Library/LaunchDaemons/limit.maxfiles.plist
  1. 加载plist文件(或重启系统后生效 launchd在启动时会自动加载该目录的plist)
sudo launchctl load -w /Library/LaunchDaemons/limit.maxfiles.plist
  1. 确认更改后的限制
launchctl limit maxfiles

详见Mac OS X下的资源限制

macOS定时任务

参考Scheduling Timed Jobs

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
        <key>Label</key>
        <string>com.arloor.job</string>
        <!-- 加载后立即启动,即开机自启 -->
        <key>RunAtLoad</key>
        <true />
        <key>WorkingDirectory</key>
        <string>/tmp</string>
        <key>ProgramArguments</key>
        <array>
            <string>/Users/arloor/bin/work</string>
        </array>
        <key>StartCalendarInterval</key>
        <dict>
            <!-- 每天10点 -->
            <key>Hour</key>
            <integer>10</integer>
            <key>Minute</key>
            <integer>0</integer>
        </dict>
        <!-- 标准输出路径 -->
        <key>StandardOutPath</key>
        <string>/tmp/work.log</string>
    </dict>
</plist>
  1. 这样设置后,每天10点会执行一次。
  2. 如果10点刚好mac在待机,则唤醒后会执行一次。
  3. 如果10点是关机的,则开机后不会执行。
  4. 还有个StartInterval的参数,每多少秒执行一次。这个参数因睡眠导致的错过在唤醒时不会执行的。

windows开机自启动

编写startup.vbs,放到

C:\Users\你的用户名\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup

文件夹下

startup.vbs内容如下:

set forward=WScript.CreateObject("WScript.Shell")
forward.Run "taskkill /f /im forward.exe",0,True
forward.Run "C:\Users\arloor\go\bin\forward.exe -conf D:\bin\caddyfile -log E:\data\var\log\forward.log -socks5conf=D:\bin\socks5.yaml",0
  1. 先关闭forward.exe,末尾的0,True表示不开启窗口,等待该命令结束再执行下一行
  2. 再启动forward.exe,末尾的0表示不开启窗口

具体Run命令见www.vbsedit.com