This is a riveting series:
- On the LPIC-1 Exam 102: Shells and Shell Scripting
- On the LPIC-1 Exam 102: User Interfaces and Desktops
- On the LPIC-1 Exam 102: Administrative Tasks
- On the LPIC-1 Exam 102: Essential System Services
- On the LPIC-1 Exam 102: Networking Fundamentals
- On the LPIC-1 Exam 102: Security
And, so is this one!
When studying for the Linux Professional Institute LPIC-1 certification, I took a bunch of notes when reading the docs and doing an online course. Note that this is not exhaustive, so do not depend on this article to get you prepared for the exam.
The menu items below are not in any order.
This roughly covers Topic 107: Administrative Tasks.
Caveat emptor.
- Exam Details
- Topic 107: Administrative Tasks
- Summary
- References
Exam Details
- Exam Objectives Version: 5.0
- Exam Code: 102-500
Topic 107: Administrative Tasks
/etc/login.defs
The /etc/login.defs
file defines the configuration parameters that control both the users and the groups. It is a required file.
Here are some of the parameters that are the most impactful:
Parameter | Description |
---|---|
UID_MIN and UID_MAX |
The range of user IDs that can be assigned to new ordinary users. |
GID_MIN and GID_MAX |
The range of group IDs that can be assigned to new ordinary groups. |
CREATE_HOME |
Specify whether a home directory should be created by default for new users. |
USERGROUPS_ENAB |
Specify whether the system should by default create a new group for each new user account with the same name as the user, and whether deleting the user account should also remove the user’s primary group if it no longer contains members. |
MAIL_DIR |
The mail spool directory. |
PASS_MAX_DAYS |
The maximum number of days a password may be used. |
PASS_MIN_DAYS |
The minimum number of days allowed between password changes. |
PASS_MIN_LEN |
The minimum acceptable password length. |
PASS_WARN_AGE |
The number of warning days before a password expires. |
But different utilities will use access and use different parameters (see /etc/default/useradd
).
Users
The default values used by both the user and group utilities are set in /etc/default/useradd
and /etc/login.defs
.
useradd
useradd
is the lower-level utility for creating users from the command-line that allows greater flexibility and customization than its higher-level brother, adduser
.
By default, a same-named group will also be created. In addition, the UID
will be the next sequentially available number unless one is specified upon creation (with the -u
option).
There are many options, but here are the most commonly-used ones:
Option | Description |
---|---|
-c , --comment |
create with custom comments (i,e., the full name of the user) |
-d , --home-dir |
create with a custom home directory |
-e , --expiredate |
create by setting a specific date on which it will be disabled |
-f , --inactive |
create by setting the number of days after a password expires during which the user should update the password (otherwise the account will be disabled) |
-g , --gid |
create with a specific GID |
-G , --groups |
create by adding it to multiple secondary groups |
-k , --skel |
create by copying the skeleton files from a specific custom directory (this option is only valid if the -m or --create-home option is specified) |
-K , --key |
overrides /etc/login.defs defaults (UID_MIN , UID_MAX , UMASK , PASS_MAX_DAYS and others) 1 |
-m , --create-home |
create with its home directory (if it does not exist) |
-M , --no-create-home |
create without its home directory |
-s , --shell |
create with a specific login shell |
-u , --uid |
create with a specific UID 2 |
-Z , --selinux-user |
the SELinux user for the user’s login 3 |
- 1
- for example:
-K PASS_MAX_DAYS=-1
-K UID_MIN=100 -K UID_MAX=499
- for example:
- 2
- must be unique, unless the
-o
(or--non-unique
) option is used - the value must be non-negative
- the default is to use the smallest
ID
value greater than or equal toUID_MIN
and greater than every other user (defined in/etc/login.defs
)
- must be unique, unless the
- 3
- the default is to leave blank and have the system select the default
SELinux
user
- the default is to leave blank and have the system select the default
Also, invoking useradd
with only the -D
(also, --defaults
) switch will display the default values:
$ sudo useradd -D
GROUP=100
HOME=/home
INACTIVE=-1
EXPIRE=
SHELL=/bin/sh
SKEL=/etc/skel
CREATE_MAIL_SPOOL=no
So, the newly-created user account will get these defaults plus whichever ones are specified on the command line. Of course, any given options will override the defaults, if there is a conflict.
Any groups to which the new user will belong must already exist.
Once the account is created, use passwd
to set the user’s password.
When using this utility, the password and group databases are updated with the new information.
usermod
The usermod
utility is used to change attributes of an extant account. Here are some of the most common options:
Option | Description |
---|---|
-c , --comment |
add a brief comment to the specified user account |
-d , --home |
change the home directory of the specified user account 1 |
-e , --expiredate |
set the expiration date of the specified user account |
-f , --inactive |
set the number of days after a password expires during which the user should update the password (otherwise the account will be disabled) |
-g , --gid |
change the primary group of the specified user account (the group must exist) |
-G , --groups |
add secondary groups to the specified user account 2 |
-l , --login |
change the login name of the specified user account |
-L , --lock |
lock the specified user account 3 |
-m , --move-home |
move the content of the user’s home directory 4 |
-s , --shell |
change the login shell of the specified user account |
-u , --uid |
change the UID of the specified user account |
-U , --unlock |
unlock the specified user account 5 |
-Z , --selinux-user |
the new SELinux user for the user’s login 6 |
- 1
- when used with the
-m
(also,--move-home
) option, the contents of the current home directory are moved to the new home directory, which is created if it does not already exist
- when used with the
- 2
- each group must exist and must be separated from the next by a comma, with no intervening whitespace
- if used alone, this option removes all existing groups to which the user belongs
- when used with the
-a
(also,--append
) option, it simply appends new secondary groups to the existing ones
- 3
- puts an exclamation mark in front of the encrypted password within the
/etc/shadow
file - this disables access with a password
- puts an exclamation mark in front of the encrypted password within the
- 4
- only valid when coupled with the
-d
(or--home
) option
- only valid when coupled with the
- 5
- this removes the exclamation mark in front of the encrypted password in the
/etc/shadow
file
- this removes the exclamation mark in front of the encrypted password in the
- 6
- A blank
SEUSER
will remove theSELinux
user mapping for userLOGIN
(if any)
- A blank
Many of them perform the same actions as those of the
useradd
utility.
userdel
Let’s now briefly look at the userdel
tool.
When using this utility, the password and group databases remove references to this account.
Here are some common options:
Option | Description |
---|---|
-r , --remove |
files in the user’s home directory will be removed along with the home directory itself and the user’s mail spool 1 |
-Z , --selinux-user |
remove any SELinux user mapping for the user’s login. |
- 1
- files located in other file systems will have to be searched for and deleted manually
- the mail spool is defined by the
MAIL_DIR
variable in thelogin.defs
file
passwd
Use the passwd
utility to change a user’s password. Must be root
to be able to change any user’s password on the system.
Interestingly, the passwd
binary has its SUID
bit set. Without this, a regular user would not be able to change their own password.
$ ls -l $(which passwd)
-rwsr-xr-x 1 root root 63960 Feb 7 2020 /usr/bin/passwd
By far, the most common use case for this utility is just to call it as a regular user without any options or arguments.
However, it is possible to pass it a number of options that can change the user password expiry information, much like the chage
utility:
Option | Description |
---|---|
-d |
delete the password of a user account (thus disabling the user) |
-e |
force the user account to change the password |
-i |
the number of days of inactivity after a password expires during which the user should update the password (otherwise the account will be disabled) |
-l |
lock the user account (the encrypted password is prefixed with an exclamation mark in the /etc/shadow file) |
-n |
the minimum password lifetime |
-S |
output information about the password status of a specific user account |
-u |
unlock the user account (the exclamation mark is removed from the password field in the /etc/shadow file) |
-x |
the maximum password lifetime |
-w |
the number of days of warning before the password expires during which the user is warned that the password must be changed |
chage
The chage
utility stands for “change age”. This is used to change the password aging and expiry information of a given user.
Much of the same information that we just saw can be modified with the passwd
utility can also be modified with this tool.
Here are some useful options:
Option | Description |
---|---|
-d |
Set the last password change for a user account. |
-E |
Set the expiration date for a user account. |
-I |
Set the number of days of inactivity after a password expires during which the user should update the password (otherwise the account will be disabled). |
-m |
Set the minimum password lifetime for a user account. |
-M |
Set the maximum password lifetime for a user account. |
-W |
Set the number of days of warning before the password expires during which the user is warned that the password must be changed. |
In addition, a user can list their expiry information. This is the only thing that a regular user can do with the chage
utility.
$ chage -l btoll
Last password change : Jan 02, 2023
Password expires : never
Password inactive : never
Account expires : never
Minimum number of days between password change : 0
Maximum number of days between password change : 99999
Number of days of warning before password expires : 7
Groups
The default values used by both the user and group utilities are set in /etc/default/useradd
and /etc/login.defs
.
groupadd
Just as with users, when adding a group with the groupadd
utility, the GID
will be the next sequentially available number unless specfied by the -g
option.
Here are some of the most common options used with the tool:
-g
,--gid
- the numerical value of the group’s ID- must be unique, unless the
-o
option is used - the value must be non-negative
- the default is to use the smallest
ID
value greater than or equal toGID_MIN
and greater than every other group (defined in/etc/login.defs
)
- must be unique, unless the
-K
,--key
- overrides/etc/login.defs
defaults (GID_MIN
,GID_MAX
and others)-K GID_MIN=100 -K GID_MAX=499
-r
,--system
- create a system group
groupmod
Use the groupmod
utility to change the attributes of existing groups.
-g
,--gid
- the groupID
of the givenGROUP
will be changed toGID
- the value of
GID
must be a non-negative decimal integer - must be unique, unless the
-o
option is used. - users who use the group as primary group will be updated to keep the group as their primary group
- any files that have the old group
ID
and must continue to belong toGROUP
must have their groupID
changed manually
- the value of
-n
,--new-name
- the name of the group will be changed fromGROUP
toNEW_GROUP
name
$ sudo groupmod -n NEW_NAME -g NEW_GUID OLD_NAME
groupdel
Deleting the group using the groupdel
utility usually isn’t passed any option (at least, in the common scenarios). Simply, pass the group name as the sole argument:
$ sudo groupdel devops
Interestingly, a group cannot be deleted if it is the primary group of any user on the system.
gpasswd
The gpasswd
utility can be used to remove a password that had been assigned to a group upon its creation. In practice, though, this it is rarely a good idea to assign a password to a group, as all members of the group would then need to know the password. And as everyone knows, people can’t be trusted with anything.
Additionally, gpasswd
can be used to administer /etc/group
by assigning and removing users to groups and adding group administrators.
Here are some common options:
-a
,--add
- add the user to the named group-d
,--delete
- remove the user from the named group-r
,--remove-password
- remove the password from the named group- the group password will be empty
- only group members with a password will be allowed to use
newgrp
to join the named group
-R
,--restrict
- restrict the access to the named group- the group password is set to “!”
-A
,--administrators
- set the list of administrative users-M
,--members
- set the list of group members
Skeleton Directory
See SKEL
.
Important User and Group Files
These files cannot be edited directly. Instead, use either the command-line tools or the GUI
tools. But don’t use the GUI
tools.
/etc/passwd
The /etc/passwd
file contains basic information about users, formatted by seven colon-delimited fields.
It will have default permissions of 0644
.
Let’s look at an entry in the file and break it down:
$ grep btoll /etc/passwd
btoll:x:1000:1000:Benjamin Toll,,,:/home/btoll:/bin/bash
Below is a description of each field:
btoll = username
x = user password (the actual password is in /etc/shadow)
1000 = UID
1000 = GID of primary group
Benjamin Toll,,, = gecos field (can contain multiple comma-separated values)
/home/btoll = home directory
/bin/bash = user's shell
Let’s modify the gecos
field using the chfn
utility to fill out the entry and view the entry again:
$ chfn
Password:
Changing the user information for btoll
Enter the new value, or press ENTER for the default
Full Name: Benjamin Toll
Room Number []: 2112
Work Phone []: 123-456-7890
Home Phone []: 123-456-7890
$ grep btoll /etc/passwd
btoll:x:1000:1000:Benjamin Toll,2112,123-456-7890,123-456-7890:/home/btoll:/bin/bash
Next, with usermod
:
$ sudo usermod -c "here is a little comment" btoll
$ grep btoll /etc/passwd
btoll:x:1000:1000:here is a little comment:/home/btoll:/bin/bash
Weeeeeeeeeeeeeeeeeeeeeeeeeee
/etc/group
The /etc/group
file contains basic information about groups, formatted by four colon-delimited fields.
It will have default permissions of 0644
.
Let’s take at one of the groups in this file to which I belong, shall we? We shall.
$ grep audio /etc/group
audio:x:29:pulse,btoll
Let’s break down those four colon-separated fields:
audio = group
x = group password (the actual password is in /etc/gshadow)
29 = GID
pulse,btoll = comma-delimited list of users belonging to the group, except those for whom this is the primary group
/etc/shadow
The /etc/shadow
file contains encrypted user passwords, formatted by nine colon-delimited fields. It is only readable by root
and privileged users.
It will have default permissions of 0640
.
Let’s take a look at an entry and do that explaining stuff for the nine fields:
sudo grep kilgore /etc/shadow
kilgore:$6$xyz$suIWtAFVHRykWhrUWmzhdCEWL4VvADYdUiEwjICyUkWot5jssVG/wnn9ww5ZQzhVELGhqvSVsUpVRkiqckDAp1:19359:0:99999:7:::
kilgore = username
$6$xy... = encrypted password (locked if the first char is `!`)
19359 = date of last password change (number of days since 01/01/1970)
0 = minimum password age
99999 = maximum password age
7 = password warning period
= password inactivity period
= account expiration date
= reserved field (for future use)
Note that the last three fields are empty.
/etc/gshadow
The /etc/gshadow
file contains encrypted group passwords, formatted by four colon-delimited fields. It is only readable by root
and privileged users.
It will have default permissions of 0640
.
$ sudo grep audio /etc/gshadow
audio:*::pulse,btoll
Let’s break down those four colon-separated fields:
audio = group
* = the encrypted password for the group (it is used when a user, who is not a member of the group, wants to join the group using the newgrp command — if the password starts with !, no one is allowed to access the group with newgrp)
= a comma-delimited list of the administrators of the group (they can change the password of the group and can add or remove group members with the gpasswd command)
pulse,btoll = comma-delimited list of users belonging to the group
From the
/etc/gshadow
man page:If the password field contains some string that is not a valid result of
crypt(3)
, for instance!
or *, users will not be able to use a unix password to access the group (but group members do not need the password).
getent
and nsswitch.conf
Yes, we can use tools like grep
and ag
to get the information we want, but it’d be quicker and easier to get it using the getent
utility which gets its information databases supported by the Name Service Switch libraries that are listed in the /etc/nsswitch.conf
file.
For instance, one of the databases listed in the first column in the nsswitch.conf
config file below is the hosts
database. The remaining columns for that entry describe the order of sources to query and a limited set of actions that can be performed by lookup result.
... hosts: files mdns4_minimal [NOTFOUND=return] dns myhostname mymachines ...
Let’s look at how we can get the same information that we got using grep
filtering above:
$ getent passwd btoll
btoll:x:1000:1000:here is a little comment:/home/btoll:/bin/bash
$ getent group audio
audio:x:29:pulse,btoll
$ sudo getent shadow kilgore
kilgore:$6$xyz$suIWtAFVHRykWhrUWmzhdCEWL4VvADYdUiEwjICyUkWot5jssVG/wnn9ww5ZQzhVELGhqvSVsUpVRkiqckDAp1:19359:0:99999:7:::
$ sudo getent gshadow audio
audio:*::pulse,btoll
You can pass more than one key to a database (i.e.,
getent passwd btoll root ...
).
Here’s the full file on my Debian bullseye
system:
/etc/nsswitch.conf
# /etc/nsswitch.conf # # Example configuration of GNU Name Service Switch functionality. # If you have the `glibc-doc-reference' and `info' packages installed, try: # `info libc "Name Service Switch"' for information about this file. passwd: files group: files shadow: files gshadow: files hosts: files mdns4_minimal [NOTFOUND=return] dns myhostname mymachines networks: files protocols: db files services: db files ethers: db files rpc: db files netgroup: nis
The following files are read when “files” source is specified for respective databases (the database is the first column):
aliases /etc/aliases
ethers /etc/ethers
group /etc/group
hosts /etc/hosts
initgroups /etc/group
netgroup /etc/netgroup
networks /etc/networks
passwd /etc/passwd
protocols /etc/protocols
publickey /etc/publickey
rpc /etc/rpc
services /etc/services
shadow /etc/shadow
So, we can see that the following databases all get their information from their respective file and no other source:
passwd
group
shadow
gshadow
Lastly, the following two commands are roughly equivalent:
$ cat /etc/protocols
$ getent protocols
Scheduling Jobs
cron
The cron
utility expects that the system will always be up and running. It has no mechanism to run jobs that were missed because the system was powered off.
The cron
daemon checks tables known as crontab
s for jobs to run. There are two kinds: user and system.
By default, the output will be emailed to the user that owns the crontab
. A common use case is to have the job redirect stdout
to a file or even /dev/null
and to have stderr
sent to their email.
User Crontabs
A user crontab(5)
is a text file in which a user can define jobs to run for any purpose that suits them. It is managed via the crontab(1)
utility and has a very specific format, which we’ll look at momentarily.
crontab(1)
is the tool that acts as a frontend to the user’s crontab(5)
file. On my system (Debian bullseye
), this file resides in /var/spool/cron/crontabs/
, although it may be different depending on the Linux distribution.
Common switches to crontab(1)
:
-e
- edit thecrontab
- will create if it doesn’t already exist and automatically install in
/var/spool/cron/crontabs/
$ sudo ls /var/spool/cron/crontabs/ $ crontab -e no crontab for btoll - using an empty one crontab: installing new crontab $ sudo ls /var/spool/cron/crontabs/ btoll
- uses the editor specified by either the
VISUAL
orEDITOR
environment variable to edit thecrontab
(if neither are defined, then the default editor at/usr/bin/editor
is used – probablynano
)$ readlink -f /usr/bin/editor /usr/bin/nano $ $ ls -l /usr/bin/editor lrwxrwxrwx 1 root root 24 Jan 2 16:59 /usr/bin/editor -> /etc/alternatives/editor* $ ls -l /etc/alternatives/editor lrwxrwxrwx 1 root root 9 Jan 2 16:59 /etc/alternatives/editor -> /bin/nano*
- will create if it doesn’t already exist and automatically install in
-i
- interactive (combine with-r
)$ crontab -ir crontab: really delete btoll's crontab? (y/n)
-l
- printscrontab
tostdout
-r
- remove thecrontab
-u
- its value is the user’scrontab
to be edited (needs privileged permissions)
And here is an example of a tar
command that will run every Monday at 5am:
0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
Let’s look at the format of a user’s crontab
file:
- the minute of the hour (0-59)
- the hour of the day (0-23)
- the day of the month (1-31)
- can use the first three letters of the name
- the month of the year (1-12)
- can use the first three letters of the name
- the day of the week (0-7 with Sunday=0 or Sunday=7)
- the command to run
Multiple values can be expressed using the following special characters:
*
(asterisk) - refers to any value,
(comma) - a list of possible values0,15,30,45 8 * * 2 ./cmd
- the command is run every quarter hour at 8am every Tuesday
-
(dash) - a range of possible values0 2-4,14-16 * * * ./cmd
- the command is run every day at the top of hours 2am-4am and 2pm-4pm
/
(slash) - stepped values0 0-23/2 * * * ./cmd
- the command is run every day at midnight, 2am, 4am, 6am, etc.
There are nice web-based utilities like
crontab guru
that can assist in making sure that you get the exact time that you want.
Note that it’s always preferable to edit a user’s crontab
using the crontab(5)
tool rather than editing them directly in /var/spool/cron/crontabs/
(usually, the permissions on the crontab
files themselves only allow them to be edited using crontab(1)
).
System Crontabs
Unlike, user crontab
s, system crontab
s can only be edited by a privileged user. The main system crontab
is /etc/crontab
and others can be installed in /etc/cron.d/
.
In addition, there are also locations in which you can place scripts to be run by cron
(and anacron
, to be precise). These are in the following directories:
/etc/cron.daily/
/etc/cron.hourly/
/etc/cron.monthly/
/etc/cron.weekly
These are self-explanatory. For example, if you want something to run monthly, simply drop the script in /etc/cron.monthly/
. That’s a bingo!
Note that
crontabs
are in/etc/cron.d/
but scripts are in/etc/cron.{daily,hourly,monthly,weekly}
.
The format of the system crontab
files is exactly the same as that of the user crontab
except for the addition of the user
field (the sixth field, seen below in bold):
- the minute of the hour (0-59)
- the hour of the day (0-23)
- the day of the month (1-31)
- can use the first three letters of the name
- the month of the year (1-12)
- can use the first three letters of the name
- the day of the week (0-7 with Sunday=0 or Sunday=7)
- the name of the user account to be used when executing the command
- the command to run
The same special characters apply as well, allowing a job to be run with the same precision as a user job.
Unlike user crontab
s, system crontab
s are edited directly and not through the crontab(1)
tool. This applies, of course, to /etc/crontab
(the system crontab
) and any file in /etc/cron.d/
.
Crontab Variables
HOME
- the directory where cron invokes the commands (by default the user’s home directory)MAILTO
- the name of the user or the address to which the standard output and error is mailed (by default the crontab owner). Multiple comma-separated values are also allowed and an empty value indicates that no mail should be sentPATH
- the path where commands can be foundSHELL
- the shell to use (by default /bin/sh)
Also, there are extensions that are “nicknames”, which replace the first five fields and represent some common cases:
From the crontab(5)
man page:
@reboot : Run once after reboot. @yearly : Run once a year, ie. "0 0 1 1 *". @annually : Run once a year, ie. "0 0 1 1 *". @monthly : Run once a month, ie. "0 0 1 * *". @weekly : Run once a week, ie. "0 0 * * 0". @daily : Run once a day, ie. "0 0 * * *". @hourly : Run once an hour, ie. "0 * * * *".
Access to Cronjobs
There are two files that control access to the crontab
command. The first is /etc/cron.allow
and the second is /etc/cron.deny
, although they many not exist on your system. They don’t on mine (Debian bullseye
):
$ ls /etc/cron.{allow,deny}
ls: cannot access '/etc/cron.allow': No such file or directory
ls: cannot access '/etc/cron.deny': No such file or directory
If one or both do exist, they should contain a list of users, one per line.
If cron.allow
exists, then all listed users have access to crontab
. Note that if the same user appears in both files that they will be allowed to created cron jobs, because cron
won’t check cron.deny
if the given user is first found in cron.allow
.
Inversely, if only cron.deny
exists, then only the listed users are denied created cron jobs.
anacron
The anacron
utility is more flexible than cron
in that, unlike cron
, anacron
does not expect that the system will be continuously running.
Although anacron
is out of scope for the LPIC-1
certification, it’s worth looking into. Start with the man page and then look at how it’s currently being used in /etc/crontab
:
17 * * * * root cd / && run-parts --report /etc/cron.hourly 25 6 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily ) 47 6 * * 7 root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly ) 52 6 1 * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
Interesting how cron
will test for the anacron
binary for all scripts except the hourly ones, hmm? Hmmmmmmm?
systemd.timer
Of course, systemd
would have units to control and manage timers. These units are called, well, timers, and you can see a list of all the timers currently in memory using the list-timers
command:
$ systemctl list-timers
NEXT LEFT LAST PASSED UNIT ACTIVATES
Sun 2023-01-29 20:40:00 EST 4min 22s left Sun 2023-01-29 20:32:54 EST 2min 42s ago sysstat-collect.timer sysstat-collec>
Sun 2023-01-29 21:32:27 EST 56min left Sun 2023-01-29 20:34:37 EST 1min 0s ago anacron.timer anacron.service
Mon 2023-01-30 00:00:00 EST 3h 24min left Sun 2023-01-29 01:45:46 EST 18h ago exim4-base.timer exim4-base.ser>
Mon 2023-01-30 00:00:00 EST 3h 24min left Sun 2023-01-29 01:45:46 EST 18h ago logrotate.timer logrotate.serv>
Mon 2023-01-30 00:00:00 EST 3h 24min left Sun 2023-01-29 01:45:46 EST 18h ago man-db.timer man-db.service
Mon 2023-01-30 00:00:00 EST 3h 24min left Sun 2023-01-29 01:45:46 EST 18h ago mlocate.timer mlocate.service
Mon 2023-01-30 00:07:00 EST 3h 31min left n/a n/a sysstat-summary.timer sysstat-summar>
Mon 2023-01-30 00:12:08 EST 3h 36min left Sun 2023-01-29 13:28:22 EST 7h ago fwupd-refresh.timer fwupd-refresh.>
Mon 2023-01-30 00:28:19 EST 3h 52min left Mon 2023-01-23 01:00:46 EST 6 days ago fstrim.timer fstrim.service
Mon 2023-01-30 06:08:55 EST 9h left Sun 2023-01-29 13:10:34 EST 7h ago apt-daily-upgrade.timer apt-daily-upgr>
Mon 2023-01-30 06:18:06 EST 9h left Sun 2023-01-29 13:10:34 EST 7h ago apt-daily.timer apt-daily.serv>
Mon 2023-01-30 20:28:09 EST 23h left Sun 2023-01-29 20:15:55 EST 19min ago systemd-tmpfiles-clean.timer systemd-tmpfil>
Sun 2023-02-05 03:10:59 EST 6 days left Sun 2023-01-29 13:10:34 EST 7h ago e2scrub_all.timer e2scrub_all.se>
13 timers listed.
Pass --all to see loaded but inactive timers, too.
You can also use list-units
:
$ systemctl list-units --type timer
UNIT LOAD ACTIVE SUB DESCRIPTION
anacron.timer loaded active waiting Trigger anacron every hour
apt-daily-upgrade.timer loaded active waiting Daily apt upgrade and clean activities
apt-daily.timer loaded active waiting Daily apt download activities
e2scrub_all.timer loaded active waiting Periodic ext4 Online Metadata Check for All Filesystems
exim4-base.timer loaded active waiting Daily exim4-base housekeeping
fstrim.timer loaded active waiting Discard unused blocks once a week
fwupd-refresh.timer loaded active waiting Refresh fwupd metadata regularly
logrotate.timer loaded active waiting Daily rotation of log files
man-db.timer loaded active waiting Daily man-db regeneration
mlocate.timer loaded active waiting Updates mlocate database every day
sysstat-collect.timer loaded active waiting Run system activity accounting tool every 10 minutes sysstat-summary.timer loaded active waiting Generate summary of yesterday's process accounting
systemd-tmpfiles-clean.timer loaded active waiting Daily Cleanup of Temporary Directories
LOAD = Reflects whether the unit definition was properly loaded.
ACTIVE = The high-level unit activation state, i.e. generalization of SUB.
SUB = The low-level unit activation state, values depend on unit type.
13 loaded units listed. Pass --all to see loaded but inactive units, too.
To show all installed unit files use 'systemctl list-unit-files'.
Now, we’ll take a look at one of the timers, apt-daily-upgrade.timer
:
$ systemctl cat apt-daily-upgrade.timer
# /lib/systemd/system/apt-daily-upgrade.timer
[Unit]
Description=Daily apt upgrade and clean activities
After=apt-daily.timer
[Timer]
OnCalendar=*-*-* 6:00
RandomizedDelaySec=60m
Persistent=true
[Install]
WantedBy=timers.target
And the service that is activated when the timer expires:
$ systemctl cat apt-daily-upgrade
# /lib/systemd/system/apt-daily-upgrade.service
[Unit]
Description=Daily apt upgrade and clean activities
Documentation=man:apt(8)
ConditionACPower=true
After=apt-daily.service network.target network-online.target systemd-networkd.service NetworkManager.service connman.service
[Service]
Type=oneshot
ExecStartPre=-/usr/lib/apt/apt-helper wait-online
ExecStart=/usr/lib/apt/apt.systemd.daily install
KillMode=process
TimeoutStopSec=900
You would enable and start the timer unit like you would any other unit:
# systemctl enable foobar.timer
# systemctl start foobar.timer
Also, make sure you invoke systemctl daemon-reload
whenever you modify a unit.
In addition to real-time timers, systemd
also supports monotonic timers, which are timers that define jobs that are scheduled after a previously-defined event has occurred, like system boot.
To mimic a cron
job, you’d want to use the OnCalendar
field in the [Timer]
clause, which has the following format:
DayOfWeek Year-Month-Day Hour:Minute:Second
DayOfWeek
is optional, and the special characters *
, /
and ,
have the same meaning as they do in the crontab
, with the exception of ..
, which replaces the hyphen (-
) as the way to select a contiguous range.
Additionally, there are similar “nicknames” for particular frequencies, very similar in kind that cron
uses and have been enumerated above:
hourly
daily
weekly
monthly
yearly
Here are a couple examples taken directly from my system:
$ systemctl cat logrotate.timer
...
[Timer]
OnCalendar=daily
...
$ systemctl cat apt-daily.timer
...
[Timer]
OnCalendar=*-*-* 6,18:00
...
Note the hyphens separating the date and the colons separating the time.
Lastly, timers are logged to the systemd
journal and should be viewed with journalctl
.
at
When you need to run a job only once at some point in the future, then at
is your man and depends on the atd
daemon to be running on the machine.
Here’s an example to whet your whistle:
$ at now
warning: commands will be executed using /bin/sh
at> date
at> <EOT>
job 5 at Mon Jan 30 01:10:00 2023
To exit the
at
prompt and submit the job, pressCTRL-D
.
The date
command will be executed “immediately” (since the time was now
), and the results will have been sent to my mail spool.
You have new mail in /var/mail/btoll
Yay.
Here’s something titillating. The
/var/spool/mail
directory is a symbolic link to/var/mail
:$ ls -l /var/spool/mail 3420256 lrwxrwxrwx 1 root root 7 Jan 2 16:59 /var/spool/mail -> ../mail/
Additionally, the batch
command is very similar to at
, only differing in that the job(s) will run only when the system load is low enough to permit it (when the load average drops below 1.5
or 0.8
, depending on the distribution’s binary).
batch
is just a type of queue inat
. From the man page:`
-q queue
` uses the specified queue. A queue designation consists of a single letter; valid queue designations range froma
toz
andA
toZ
. Thea
queue is the default forat
and theb
queue forbatch
. Queues with higher letters run with increased niceness. The special queue “=” is reserved for jobs which are currently running.
What are some of the most common switches? I’m glad you asked, dbag!
-c
- cats the jobs listed on the command line to standard output-d
- deletes jobs identified by their job number (is an alias foratrm
)-f
- reads the job from file rather than standard input-l
- list the user’s pending jobs and all jobs if the superuser (is an alias foratq
)-m
- send mail to the user when the job has completed even if there was no output -t time run the job at time, given in the format [[CC]YY]MMDDhhmm[.ss]-q
uses the specified queue- a queue designation consists of a single letter; valid queue designations range from
a
toz
andA
toZ
- the
a
queue is the default forat
and theb
queue forbatch
- queues with higher letters run with increased niceness
- the special queue “=” is reserved for jobs which are currently running
- a queue designation consists of a single letter; valid queue designations range from
-b
- is an alias forbatch
-r
- is an alias foratrm
-v
- shows the time the job will be executed before reading the job
Here are a couple of aliases to know about:
List the jobs:
$ atq
7 Mon Jan 30 01:57:00 2023 a btoll
$ at -l
7 Mon Jan 30 01:57:00 2023 a btoll
Delete a job:
$ atrm 8
$ at -d 8
Cannot find jobid 8
Access to at
Jobs
The two files that may or may not be on your system as relates to at
and access rights are /etc/at.allow
and /etc/at.deny
.
On my Debian bullseye
machine, I only have the latter, and there are quite a few entries in it by default:
$ sudo wc -l /etc/at.deny
24 /etc/at.deny
$ sudo head /etc/at.deny
alias
backup
bin
daemon
ftp
games
gnats
guest
irc
lp
The same rules apply as those described in the analogous files for cron
in the section Access to Cronjobs.
systemd-run
Lastly, we’ll turn to systemd
’s answer to at
: systemd-run
. This will create a transient service that will run only once, so you can think of it as analogous to at
.
Creating a transient timer unit and it’s corresponding service is easy:
$ sudo systemd-run --on-active="2m" date
Running timer as unit: run-r637233efa76940bdbe6f6598a530d776.timer
Will run service as unit: run-r637233efa76940bdbe6f6598a530d776.service
We can then use the output of the last command to lookup both the timer and the service:
$ sudo systemctl cat run-r637233efa76940bdbe6f6598a530d776.timer
# /run/systemd/transient/run-r637233efa76940bdbe6f6598a530d776.timer
# This is a transient unit file, created programmatically via the systemd API. Do not edit.
[Unit]
Description=/usr/bin/date
[Timer]
OnActiveSec=2min
RemainAfterElapse=no
$
$ sudo systemctl cat run-r637233efa76940bdbe6f6598a530d776
# /run/systemd/transient/run-r637233efa76940bdbe6f6598a530d776.service
# This is a transient unit file, created programmatically via the systemd API. Do not edit.
[Unit]
Description=/usr/bin/date
[Service]
ExecStart=
ExecStart="/usr/bin/date"
Once the timer expires and the command is run, both the timer and the service are removed from they system. It’s like they never even existed, just like decency and respect in the modern Republican party.
Lastly, as usual, the output will go to the journal.
Localization
Time Zones
Time zones are relative to the prime meridian, which is 0 degrees longitude and is called Coordinated Universal Time (UTC). Time zones don’t follow the longitudinal distance from the prime meridian exactly, instead they’re drawn to reflect the borders of countries and other significant divisions.
Time zones are divided respective to their offset relative to UTC. For example, offsets are written as GMT-5
or GMT+3
, where GMT is an acronym for Greenwich Mean Time, a synonym for UTC.
UTC is the GMT+0 time zone.
The time zone can be defined either as the full descriptive name or an offset or in the POSIX TZ format. Mine has the former:
$ cat /etc/timezone
America/New_York
If using an offset, it must be preceded by Etc
:
$ cat /etc/timezone
Etc/GMT-5
The
tzselect
tool is an easy way to find your particular time zone by its full descriptive name.
Because people move around the world frequently, both literally and virtually (think remote connections), best practice dictates that the system hardware clock be set to UTC.
Let’s take a gander at some utilities to view the time zone that’s set for the system.
Everybody knows the date
tool. In addition to viewing the time, you can also glean the time zone:
$ date
Tue 31 Jan 2023 02:59:36 PM EST
Here, the system is reporting as being within the Eastern Time Zone.
timedatectl
is another friendly tool.
$ timedatectl
Local time: Tue 2023-01-31 15:04:05 EST
Universal time: Tue 2023-01-31 20:04:05 UTC
RTC time: Tue 2023-01-31 20:04:05
Time zone: America/New_York (EST, -0500)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no
Again, we can see that the system is configured in the Eastern Time Zone (specifically, Eastern Standard Time), and by (EST, -0500)
designation it can be determined that the system is five hours behind the Coordinated Universal Time.
So, how does one set the time zone?
You can start by setting the TZ
environment variable. Use the aforementioned tzselect
for an easy way to get the value.
$ export TZ="America/New_York"
Better yet, put it in a shell startup script like .bash_profile
or .bashrc
or foo-manchu
.
Another good method is to reconfigure the tzdata
package:
$ sudo dpkg-reconfigure tzdata
Current default time zone: 'America/New_York'
Local time is now: Tue Jan 31 15:23:00 EST 2023.
Universal Time is now: Tue Jan 31 20:23:00 UTC 2023.
You can also reconfigure the
locales
package to make sure you have the locale package for any particular time zone in which you are interested:$ sudo dpkg-reconfigure locales
Of course, I’m assuming that both the tzdata
and the locales
packages have already been installed. If they haven’t been, simply install them and walk through the menus.
For packages that have already been downloaded, reconfiguring allows you to reset the defaults or choose different ones without having to remove and re-install the package.
For user applications, setting the localtime
for the system configures the system-wide time zone for the local system. It should be a symbolic link pointing to one of the time zone data files in /usr/share/zoneinfo/
. Mine is already set up correctly, but if it wasn’t I would use ln
like usual to create the soft link.
$ readlink -f /etc/localtime
/usr/share/zoneinfo/America/New_York
Incidentally, the timedatectl
utility changes the settings of /etc/localtime
from the command-line during runtime.
Language and Character Encoding
Most shell programs identify the language to use by the LANG
variable:
$ echo $LANG
en_US.UTF-8
The format is language code underscore (_
) region code period (.
) character encoding. So, in the following example, en
is the language code, US
is the region code and UTF-8
is the character encoding.
UTF
, of course, is intended to encompass and replace limited character encoding sets like ASCII
(American Standard Code for Information Interchange), which only allow for characters that can fit into 7 bits (the eighth bit was for extended sets, and just about everybody had their own, even your Uncle Jack).
The language code follows the
ISO-639
standard, and the region code follows theISO-3166
standard.
According to the LPIC-1
docs (which you should read), the file /etc/locale.conf
is where system-wide settings are configured, but I don’t have that on my system.
For systems that use systemd
, you can also use the localectl
utility to query and change the system locale:
$ localectl
System Locale: LANG=en_US.UTF-8
LANGUAGE=en_US
VC Keymap: n/a
X11 Layout: us
X11 Model: pc105
X11 Options: ctrl:nocaps
$
$ localectl set-locale LANG=en_BR.UTF-8
Use locale
to display all the relevant environment variables relating to localization:
$ locale
LANG=en_US.UTF-8
LANGUAGE=en_US
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=en_US.UTF-8
Here are some brief descriptions of each variable:
LC_COLLATE
- sets the alphabetical ordering- one of its purposes is to define the order files and directories are listed
LC_CTYPE
- sets how the system will treat certain sets of characters- it defines, for example, which characters to consider as uppercase or lowercase
LC_MESSAGES
- sets the language to display program messages (mostlyGNU
programs)LC_MONETARY
- sets the money unit and currency formatLC_NUMERIC
- sets the numerical format for non-monetary values- its main purpose is to define the thousand and decimal separators
LC_TIME
- sets the time and date formatLC_PAPER
- sets the standard paper sizeLC_ALL
- overrides all other variables, includingLANG
Setting LC_ALL
will override all of the variables, or do it piecemeal by just setting an individual variable:
$ date
Sat 28 Jan 2023 02:36:21 PM EST
$ env LC_ALL=pt_BR.UTF-8 date
sáb 28 jan 2023 14:36:28 EST
Lastly, iconv
is a utility to convert from one character set encoding to another.
Summary
Continue your journey with the fourth installment in this titillating series, On the LPIC-1 Exam 102: Essential System Services.