macOS is a Unix system. However, it doesn’t conform to many conventions of common Unix systems. This text discusses these differences, not in the implementation level, but in the perspective of technical users, e.g. system administrators and application programmers.
macOS manages users and groups by directory services instead of
/etc/passwd
and /etc/group
. Consider a local
network, for example, the one in your office. There are many computers
in the network, and each has its own files,accounts, etc. A directory
service organizes these distributed resources into one single index, and
makes them look like a directory hierarchy.
For example, /Hosts/PC1
represents a host in the
network, and /Users/Smith
represents a user in the
network.1
macOS uses the dscl
command to interact with directory
services.
To list all users on the system, we could run the following.
dscl . -list /Users
The first argument sets which data source we want to retrieve from. A dot means the local data source.
# List groups
dscl . -list /Groups
The -list
option lets dscl
print items of a
resource. Items of /Users
are all users. Items of
/Groups
are all groups.
If we want to check the information about the resource, we should use
-read
. For example, we could check the information of the
user Smith
:
# Retrieve the information of Smith
dscl . -read /Users/Smith
-read
prints keys and values of the specified resource.
We could also specify the keys we want.
# Get the primary group ID and default shell of Smith
dscl . -read /Users/Smith PrimaryGroupID UserShell
To add a resource, we use dscl . -create
. The following
command adds a new user belson
.
sudo dscl . -create /Users/belson
If we try to set a password for belson
using
sudo passwd belson
, passwd
will tell us the
user doesn’t exist. Because dscl
just adds a record to the
data source, but it doesn’t check the integrity. It doesn’t even assign
a UID for belson
. We must assign a UID and a primary group
for belson
by ourselves, after adding it to the data
source.
# UID 511, GID 20 for example
sudo dscl . -create /Users/belson UniqueID 511
sudo dscl . -create /Users/belson PrimaryGroupID 20
Now, belson
is a Unix user, and you can set a password
for him using passwd
. However, you still can’t login as
belson
, for we don’t assign him a shell.
sudo dscl . -create /Users/belson UserShell /bin/bash
We can login as belson
now, but he doesn’t have a home
directory. We must create the directory and specify it with
dscl
.
sudo dscl . -create /Users/belson NFSHomeDirectory /Users/belson
sudo mkdir /User/belson
sudo chmod -R belson:staff /User/belson
However, running dscl . -read /Groups/staff
will show
that neither the GroupMembers
nor the
GroupMembership
attribute has any information about
belson
. We have specified his primary group to
staff
. But as mentioned before, dscl
doesn’t
check the integrity. Bidirectional relationships must be maintained
manually.
To solve the user-group relationship problem, we could use
dseditgroup
. The following command adds belson
to the staff
group, and it takes care of the integrity.2
sudo dseditgroup -o edit -a belson -t user staff
We should make a summary here. To create a user belson
in the group staff
, we need:
sudo dscl . -create /Users/belson
sudo dscl . -create /Users/belson UniqueID 511
sudo dscl . -create /Users/belson PrimaryGroupID 20
sudo dscl . -create /Users/belson UserShell /bin/bash
sudo dscl . -create /Users/belson NFSHomeDirectory /Users/belson
sudo dseditgroup -o edit -a belson -t user staff
sudo mkdir /User/belson
sudo chmod -R belson:staff /User/belson
It is painful for a system administrator to type so many commands just to create a new user.
Since Mac OS X 10.10, the sysadminctl
command is
provided. It’s a high-level interface of dscl
. Type
sysadminctl
in the terminal to see its usage3.
We use sysadminctl
to delete belson
first.
sudo sysadminctl -deleteUser belson -secure
Using dscl
to check the directory service, we could find
that everything we did is removed.
Now, we add belson
again with
sysadminctl
:
sudo sysadminctl -addUser belson
sudo dseditgroup -o edit -a belson -t user staff
If you check the root directory, you will see some traditional Unix
things like /usr
, /var
, and /etc
.
You may also find mac-specified things like /Library
/Applications
, and /cores
.
A list of mac-specified directories with their purposes is here4.
/private | Contains tmp ,
var , and etc . /tmp ,
/var , /etc are symbolic links to them. |
/Library | Contains support files for locally installed applications, among other things. |
/System | Contains a subdirectory
Library that holds support files for the system and system
applications, among other things. |
/Network | Contains network-mounted Application, Library, and Users directories, as well as a Servers directory that contains directories mounted by the auto mount daemon. |
/Users | Contains home directories for the users on
the system. The root user’s home directory is /var/root
(actually /private/var/root ). |
/Volumes | Contains all visible mounted file systems, including removable media and mounted disk images. |
We could make a list to compare some of them with Linux.
macOS | Linux |
---|---|
/System/Library | /lib |
/Library | /usr/lib, /usr/local/lib |
/Users | /home |
/Volumes | /mnt, /media |
You may be confused here for the difference between
/Library
and /usr/lib
. Which directory should
we put libraries into?
/Library
is for macOS-specified software, like the data
of things you download from App Store, and BSD programs shipped with
macOS, like modules of pre-installed Perl and of pre-installed
Python.
/usr/lib
and /usr/local/lib
are for Unix
programs installed by yourself.
The init program of macOS is launchd
5.
The launchd program manages two types of services,
LaunchDaemon
and LaunchAgent
.
LaunchDaemon
will be started after the system is booted.
LaunchAgent
will be started after a user is logged-in.
If a program needs to be launched after the system is booted, a
.plist
file is required to be placed into
/Library/LaunchDaemons
or
/System/LaunchDaemons
.
If a program needs to be launched after a user logged-in, a
.plist
file is required to be placed into
/Library/LaunchAgents
or /System/LaunchAgents
,
or ~/Library/LaunchAgents
of the user.
The launchctl
command is used to control services of
launchd
.
# to enable sshd
sudo launchctl load -w /System/Library/LaunchDaemons/ssh.plist
# to disable sshd
sudo launchctl unload -w /System/Library/LaunchDaemons/ssh.plist
There is no /usr/include
in macOS. Headers are in the
package of Xcode. Thus your compiler may complain about it. You can run
xcrun --show-sdk-path
to print the path6.
$ xcrun --show-sdk-path
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk
Then you can use -isysroot
to specify the path for the
compiler, for example:
gcc -isysroot `xcrun --show-sdk-path` hello.c
macOS has no default package manager. Homebrew is the most popular third-party package manager for macOS.
Homebrew is good for many people, but not for one who wants things to go more like common Unix systems. Homebrew assumes you are the only user of the system, but Unix users often share a machine.
Homebrew can’t run as root. If you want to install python, you run
brew install python
instead of
sudo brew install python
. Homebrew installs packages to
/usr/local
. To achieve its sudo-free goal, Homebrew changes
the ownership of /usr/local
to the user who installed
Homebrew.
This brings security issues in a shared system. Considering a malware
faking gcc
to infect all the code you compile. Without what
Homebrew does, it must be run as root to achieve its goal, but now, even
it’s run as a normal user, it can write to
/usr/local/bin/gcc
.
Besides the security issue, Homebrew can bring another issue, if
there are multiple administrators in a shared system. Two administrators
both want to use Homebrew to install packages, but
/usr/local/bin
can belong to only one of them. So another
administrator will fail. If he or she tries to use Homebrew with
sudo
, he or she will also fail. Because Homebrew will check
that if it’s run as root. If it is, Homebrew will refuse to work unless
Homebrew itself belongs to root.
It seems the way to use Homebrew on a shared system is to install
Homebrew by root and use it with sudo
. However, Homebrew
strongly recommends us not to give it the root privilege. The best
practice mentioned in the Homebrew documentation is quoted below.
If you need to run Homebrew in a multi-user environment, consider creating a separate user account especially for use of Homebrew.
This means every time we want to use Homebrew, we must switch the account first.
$ sudo -u brewuser brew install python
If you’re not a fan of Homebrew, there are other package managers.
MacPorts is one of them and it may be more appealing by Unix users. It
requires sudo
to install packages and installs them to
/opt
. Thus you can install self-compiled packages to
/usr/local
.
Andrew. Managing users and groups from the OS X terminal <http://ajmccluskey.com/2015/01/managing-users-and-groups-from-the-os-x-terminal/>↩︎
How to add user to a group from Mac OS X command line? <https://superuser.com/questions/214004/how-to-add-user-to-a-group-from-mac-os-x-command-line>↩︎
Charles S. Edge. Using sysadminctl on macOS <https://krypted.com/mac-os-x/using-sysadminctl-macos/>↩︎
Jepson; Rothman; Rosen. Mac OS X for Unix Geeks.↩︎
Apple Inc. Creating Launch Daemons and Agents <https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html>↩︎
Apple Inc. Xcode 10 Release Notes #3035624 <https://developer.apple.com/documentation/xcode_release_notes/xcode_10_release_notes#3035624>↩︎