前言

在维护 Jenkins Slave Node(这里指 macOS 构建机)的过程,不可避免地是你会遇到一些文件访问权限和进程常驻的问题。所以,如果要解决这些问题,就要求你了解 Linux 文件访问权(包括文件和目录,以下统称文件)和什么是守护进程(macOS launchd)。

那么,回到今天本文,也将会从常用 Linux 文件访问权相关命令开始,一步一步带你了解这些其中的所以然。

一、Linux 文件访问权相关命令

首先,我们需要建立的基础认知是在 Linux 系统中,文件的访问者身份划分这 3 类:

  • 文件属主,某个用户,例如 root
  • 文件所属组,某个用户组,例如 staffwheel
  • 其他用户,不是上面 2 者

下面,我们再来分别认识下在查看和修改文件访问权过程会使用到的命令。

1.1 ls -l, ls -la

ls 这个命令,我想应该很多同学都知道,可以用于查看某个目录下有哪些文件,例如:

wujingchang@wujingchangdeMacBook-Pro ant-design % ls
AUTHORS.txt            README-zh_CN.md            package-lock.json
CHANGELOG.en-US.md        README.md            package.json
CHANGELOG.zh-CN.md        SECURITY.md            renovate.json
CNAME                components            scripts
CODE_OF_CONDUCT.md        docs                site
LICENSE                index-style-only.js        tests
README-ja_JP.md            index-with-locales.js        tsconfig.json
README-pt_BR.md            index.js            tsconfig.node.json
README-sp_MX.md            jest-puppeteer.config.js    typings
README-uk_UA.md            node_modules            webpack.config.js

ls-l Option 可以用于查看文件属主、所属组、读写权限和文件大小等信息:

wujingchang@wujingchangdeMacBook-Pro ant-design % ls -l
-rw-r--r--     1 wujingchang  staff    50839 11  9 12:50 AUTHORS.txt
-rw-r--r--     1 wujingchang  staff   320126 11  9 12:50 CHANGELOG.en-US.md
-rw-r--r--     1 wujingchang  staff   334949 11  9 12:50 CHANGELOG.zh-CN.md
-rw-r--r--     1 wujingchang  staff       16 11  9 12:50 CNAME
-rw-r--r--     1 wujingchang  staff     3254 11  9 11:39 CODE_OF_CONDUCT.md
-rw-r--r--     1 wujingchang  staff     1099 11  9 11:39 LICENSE
-rw-r--r--     1 wujingchang  staff    10155 11  9 12:50 README-ja_JP.md
-rw-r--r--     1 wujingchang  staff     9903 11  9 11:39 README-pt_BR.md
-rw-r--r--     1 wujingchang  staff     9801 11  9 12:50 README-sp_MX.md
-rw-r--r--     1 wujingchang  staff    10623 11  9 11:39 README-uk_UA.md
-rw-r--r--     1 wujingchang  staff     9596 11  9 11:39 README-zh_CN.md
# 省略若干目录

其中,如果当你还想隐藏文件的信息的时候则可以使用 ls -la。那么,可以看到这里主要 9 列,分别表示的含义为:读写权限信息、链接目录数和文件路径长度(例如 /demo/test 此时为 2)、属主、所属组、文件大小、创建月份、创建日、创建时间、文件名称,例如 AUTHORS.txt

# 读写权限    链接目录数和文件路径长度  属主          所属组   文件大小 创建月 日  时间   文件名
-rw-r--r--  1                      wujingchang  staff  50839   11    9  12:50 AUTHORS.txt

很显然,我们关注的是第一列(文件读写权限信息),而它则是由长度为 10 的字符组成:

  • 第 1 位 表示文件还是目录,例如文件是 -、目录是 d 和链接目录是 l
  • 第 2~4 位表示属主的权限,其中 r 表示读(Read)权限,w 表示写(Write)权限,x 表示执行(execute)权限,- 则表示无权限
  • 第 5~7 位表示所属组的权限
  • 第 8 ~ 10 位表示其他用户的权限

例如,这里 AUTHORS.txt 的读写权限信息 -rw-r--r-- 则表示:

  • - 普通文件
  • rw- 可读、可写、不可执行(因为是一个普通文件不是可执行程序)
  • r-- 可读、不可写、不可执行,也就是说同样是 staff 组的用户(除开 wujingchang)只能读这个文件不能写
  • r-- 同上

1.2 chmod

chmod">chmod 是 change mode 的缩写,表示改变文件的权限。我们可以通过 chmod + 和要获取的权限 rwx 来更改具体的文件权限,例如 chmod +w 表示增加可写的权限,反之 -w 则表示移除可写的权限。

同样是 AUTHORS.txt 文件,我们把原本的可读权限去掉:

wujingchang@wujingchangdeMacBook-Pro ant-design % chmod -r AUTHORS.txt

需要注意的是,-r 默认修改的是属主的访问权限,我们可以通过 u-r(属主)、g-r(所属组)、o-r(其他用户)的方式移除指定的读取权限。

那么,现在 AUTHORS.txt 文件的权限信息则是:

wujingchang@wujingchangdeMacBook-Pro ant-design % ls -l AUTHORS.txt
--w-------  1 wujingchang  staff  50839 11  9 12:50 AUTHORS.txt

可以看到原先的 r 都被移除了,此时,如果尝试打开 open AUTHORS.txt,则会提示:

此外,除了使用字符 rwx 的方式修改,我们还可以用数字的方式修改(4 可读,2 可写,1 可执行),例如同样是移除 AUTHORS.txt 的读权限:

wujingchang@wujingchangdeMacBook-Pro ant-design % chmod 020 AUTHORS.txt

1.3 chown

chown">chown是 change ownership 的缩写,表示我们可以更改文件的访问权。例如,我们可以修改 AUTHORS.txt 文件的属主为 root

wujingchang@wujingchangdeMacBook-Pro ant-design % sudo chown root AUTHORS.txt

注意由于 root 是超级用户,所以需要 sudo。然后,此时 AUTHORS.txt 的属主则会变成 root

wujingchang@wujingchangdeMacBook-Pro ant-design % ls -l AUTHORS.txt 
-rw-r--r--  1 root  staff  50839 11  9 12:50 AUTHORS.txt

那现在我们能使用 open AUTHORS.txt 打开吗?答案是可以,因为 wujingchangroot 属于同一个组 staff,而 -rw-r--r-- 的第 4~7 位 r-- 表示可读、不可写不可执行,并且其他用户也是 r--。所以,此时仍然是可以打开 AUTHORS.txt 文件,但是打开后会处于只读的模式:

那么,如果希望在修改文件属主的同时也修改文件所属组,可以在使用 chown 的时候通过 user:group 的方式修改:

wujingchang@wujingchangdeMacBook-Pro ant-design % sudo chown root:wheel AUTHORS.txt
wujingchang@wujingchangdeMacBook-Pro ant-design % ls -ls AUTHORS.txt   
104 -rw-r--r--  1 root  wheel  50839 11  9 12:50 AUTHORS.txt

然后,则是其他用户的可读权限,这可以通过 chmod 修改:

wujingchang@wujingchangdeMacBook-Pro ant-design % open AUTHORS.txt
wujingchang@wujingchangdeMacBook-Pro ant-design % sudo chmod o-r AUTHORS.txt
wujingchang@wujingchangdeMacBook-Pro ant-design % ls -ls AUTHORS.txt
104 -rw-r-----  1 daemon  wheel  50839 11  9 12:50 AUTHORS.txt
The application cannot be opened for an unexpected reason, error=Error Domain=NSOSStatusErrorDomain Code=-5000 "afpAccessDenied: Insufficient access privileges for operation " UserInfo={_LSLine=3863, LSErrorDict={
    Action = odoc;
    Documents =     (
        "AUTHORS.txt"
    );
    ErrorCode = "-5000";
    FullPaths =     (
        "/Users/wujingchang/Documents/repo/ant-design/AUTHORS.txt"
    );
}, _LSFunction=_LSOpenStuffCallLocal}

1.4 chgrp

chgrp">chgrp 是 change group 的缩写,表示我们可以更改文件的所属组的访问权。也就是说,除开使用 chmod 的时候指明用户组的方式修改所属组的访问权,我们还可以通过 chgrp 来直接修改 AUTHORS.txt 文件的所属组:

wujingchang@wujingchangdeMacBook-Pro ant-design % sudo chgrp wheel AUTHORS.txt 

二、Launchd

通常,我们在注册连接 Jenkins Slave Node 的时候是希望它常驻在内存中。这在 Windows 机器上,我们可以通过 Services 的方式实现。而在 macOS 上,我们可以通过launchd 提供的守护进程相关的能力实现。

launchd 则是 Apple Inc 创建的一个初始化和操作系统服务管理守护进程。在 Launchd 中有 2 个主要的程序 launchd 和 launchctl,前者用于启动系统和运行服务,后者用于控制服务。

所以,在 macOS 启动过程,会加载 launchd,launchd 则会运行 /etc/rc,扫描系统 System、用户 User 下、全局的 /Library/LaunchDaemons 和 /Library/LaunchAgents 目录下的脚本,根据需要在 plist 上调用 launchctl,然后 launchd 启动登录窗口。

其中 LaunchDaemonsLaunchAgents 处于不同的文件位置所执行的用户有所不同:

类型文件位置运行的用户
User Agents~/Library/LaunchAgents当前登录用户
Global Agents/Library/LaunchAgents当前登录用户
Global Daemons/Library/LaunchDaemonsroot 或者指定的用户
Global Agents/System/Library/LaunchAgents当前登录用户
Aystem Daemons/System/Library/LaunchDaemonsroot 或者指定的用户

可以看到,LaunchAgents 是只能以当前的登录用户运行,而 LaunchDaemons 可以指定特定的用户,并且需要注意的是只有 LaunchAgents 可以访问 macOS GUI 的。

如果,我们期望通过 launchd 运行指定的程序(launch Job 或 Service,以下统称 launch Job),则需要在上述的文件目录下配置以 .plist 扩展名的 XML 文件,这通常也被称为 Plist 文件。那么,下面我们来一起看下如何创建一个 launchd Job?

2.1 launchd Job 创建

用于创建 launchd Job 的 Plist 文件配置提供了诸多 Key-Value 来实现指定的声明,这里我们来看 4 个比较常用的 Key:

  • Label 用于表示 Daemon 的名称,唯一标识
  • ProgramArguments 用于表示 Daemon 启动时需要执行的命令行相关
  • UserName 用于表示启动 Daemon 的用户,例如你可以声明为 wujingchang
  • KeepAlive 用于表示是否 Daemon 需要按需运行还是必须运行

那么,以 Jenkins Slave Node 的连接为例,它需要我们在构建机器(macOS 机器)上通过指定的 Java 命令建立和 Jenkins 服务器的远程连接,这看起来会是这样:

java -jar agent.jar -jnlpUrl http://xxxxxxx/jenkins-agent.jnlp -secret xxxxxxxx -workDir D:\jenkins

所以,它的 lauchd Job 的配置会是:

<?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.jenkins.slavenode</string>
        <key>ProgramArguments</key>
        <array>
      <string>java</string>
      <string>-jar</string>
      <string>-jnlpUrl</string>
      <string>http://xxxxxxx/jenkins-agent.jnlp</string>
      <string>-secret</string>
      <string>xxxxxxxx</string>
      <string>-workDir</string>
      <string>D:\jenkins</string>
    </array>
    <key>UserName</key>
    <string>wujingchang</string>
        <key>KeepAlive</key>
        <true/>
    </dict>
</plist>

并且,需要注意的是 launchd Job 的 Plist 文件需要按前面提及的 LaunchDaemons 的文件位置存放(保证开机自启的行为),例如这里的 wujingchang 是当前用户,那么就把 Plist 文件放到 ~/Library/LaunchDaemons 文件目录下。

2.2 launchctl 命令使用

在创建好 launchd Job 的 Plist 文件后,后续在机器开启的过程则会执行对应的 Job。那么,也就是说我们刚创建的 launchd Job 需要等待下次开机才能运行。

所以,如果你新创建的 launchd Job 也需要马上运行,那么则可以使用 launchctl 提供的命令,常用的 launchctl 命令有:

  • launchctl load 加载启动 launchd Job
  • launchctl unload 卸载停止 launchd Job
  • launchctl list 查看已启动的访问 lauchd Job 信息

那么,回到上面的例子,我们可以使用 launchctl load 命令运行刚创建的 launch Job:

launchctl load ~/Library/LaunchDaemons/slavenode.plist

此外,我们还可以通过 brew services 相关的命令来启动和停止 launchd Job,具体大家可以通过 brew services --help 了解,这里不做展开。

结语

通过,了解文件访问权修改和 launchd 相关的知识,可以让我们在 Jenkins 排障的维护过程处理访问权限更得心应手,避免由于知识盲区的存在导致问题迟迟不能解决或者解决的成本较高的情况出现。并且,需要注意的是每个修改文件访问权相关的命令(chmodchownchgrp)都支持使用 -h Option 查看相关的介绍,例如 chown -h

wujingchang@wujingchangdeMacBook-Pro @ant-desgin % chown -h  
usage: chown [-fhnvx] [-R [-H | -L | -P]] owner[:group] file ...
       chown [-fhnvx] [-R [-H | -L | -P]] :group file ...

最后,如果文中存在表达不当或错误的地方,欢迎各位同学提 Issue ~

点赞

通过阅读本篇文章,如果有收获的话,可以点个赞,这将会成为我持续分享的动力,感谢~

我是五柳,喜欢创新、捣鼓源码,专注于源码(Vue 3、Vite)、前端工程化、跨端等技术学习和分享,欢迎关注我的微信公众号 Code center GitHub

五柳
1.1k 声望1.4k 粉丝

你好,我是五柳,希望能带给大家一些别样的知识和生活感悟,春华秋实,年年长茂。