Compare commits

..
112 Commits
Author SHA1 Message Date
Joachim Wiberg d00a53dfbf Bump version for v4.0-rc3
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-19 09:53:26 +01:00
Joachim Wiberg 99a8cf3eb7 initctl: add -1,--once flag for top command
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-16 23:09:01 +01:00
Joachim Wiberg da95c7d80e initctl: ensure lines don't overflow in top command
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-16 23:07:14 +01:00
Joachim Wiberg a9f7f692a9 initctl: add more columns to top command; VmSize, RSS, VmLib, %MEM
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-16 16:49:08 +01:00
Joachim Wiberg ce55f88a00 cgroup: add symlinks for traditional split controller world
Even systemd have collapsed a few of the cgroup controllers into a
semi-unified hierarchy and uses this approach.  We just take it to
the extreme and have collapsed all of them (like cgroup v2).

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-16 16:47:08 +01:00
Joachim Wiberg 74a26f559d initctl: refactor, split into multiple files
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-16 09:23:04 +01:00
Joachim Wiberg 08144df5ea initctl: fix %CPU listing bug and use hash table for faster lookup
Two major bugs: `if (!cg)` and missing `strdup(path)`.

Also convert to use std hash table for lookup of previous cpu load value
for a given cgroup path.  The hash table is sized after the current num.
rows on the screen -- resize currently not supported.  The value given
to hcreate() should be 25% greater than the estimated num of entries,
but we take a wild guess just to make sure we don't run out of space at
runtime.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-15 13:43:37 +01:00
Joachim Wiberg 230c64927b initctl: add very bare-bones cgroup based top command
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-15 02:21:43 +01:00
Joachim Wiberg d1e56f300d initctl: show cgroup cpu.shares and memory.usage in ps command
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-14 22:16:20 +01:00
Joachim Wiberg 9842786453 cgroups: fix cpu.shares assignment and set memory hierarchy
- Fix obvious refactor mistake in cpu.shares assignment
- For unified memory hierarchy we need to set .use_hierarch=1

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-14 12:27:46 +01:00
Joachim Wiberg 6d0c679e05 initctl: show comm and cmdline in ps output
The ps command currently only lists processes in the three main control
groups: init, system, user.  Kernel threads are not show at all.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-14 11:12:42 +01:00
Joachim Wiberg 5428dea5bf cgroup: refactor, use a unified hierarchy for finit
As of now, the default finit cgroup behavior is to use a unified
hierarchy of controllers under /sys/fs/cgroup/finit.

We mount cpu,cpuacct,cpuset,memory (if available) and gain the
ability to control our three major groups: init, system, user.

The default CPU share setup is ~10% for init and user, and 90% for
system.  These are guaranteed CPU shares to ensure we do not starve
PID 1 or user processes.  Support for configuring these limits will
be added in a later commit.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-14 11:07:13 +01:00
Joachim Wiberg 4e84ecae89 Set umask(022) early, in fs_init(), and no place else
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-14 11:05:12 +01:00
Joachim Wiberg 959c9fe5aa plugins: restore previous umask() after plugin ops
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-14 11:04:29 +01:00
Joachim Wiberg 03d8ef9c8f doc: drop malplaced comment
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-13 09:45:37 +01:00
Joachim Wiberg 084b4ce8d3 cgroup: minor refactor/simplify
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-13 09:40:52 +01:00
Joachim Wiberg 31cae6a56d plugins: dbus: simplify, use mksubsys(), run as messagebus user
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-13 09:26:14 +01:00
Joachim Wiberg 1fd45c6f2e plugins: bootmisc: refactor, use mksubys() and simplify
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-13 09:26:14 +01:00
Joachim Wiberg 46995fe446 Add mksusbys() to helpers.c, a kind-of tmpfiles.d in C
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-13 00:37:47 +01:00
Joachim Wiberg 600b2874e6 initctl: add bug report address to usage text, like finit -h
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-12 17:08:36 +01:00
Joachim Wiberg b080b970b7 initctl: don't warn on missing env file if it's optional
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-12 17:08:12 +01:00
Joachim Wiberg 6106574e13 initctl: minor refactor, use systemf() instead of snprintf+system
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-12 16:49:01 +01:00
Joachim Wiberg 2385a1209b initctl: don't fall back to default command for wrong commands
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-12 16:48:58 +01:00
Joachim Wiberg e8b942cf95 Fix regression in starting built-in services, introduced in 5b9d990
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-12 16:28:16 +01:00
Joachim Wiberg ac17d7f5a7 Silence output from gzip in logrotate()
- system may not have gzip (we're opportunistic)
- system may not have the log file (yet)

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-12 13:26:50 +01:00
Joachim Wiberg 1edc4356a6 initctl: improve error message for unprivileged users
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-12 13:26:16 +01:00
Joachim Wiberg 9f06a2db99 Silence developer API debug message
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-12 13:25:50 +01:00
Joachim Wiberg cfb3a9f2b9 plugins: pidfile: reduce log noise
With the new practise of keeping record of process pid files, we no
longer need to log/update when reading back the same PID we already
have on file.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-12 00:35:13 +01:00
Joachim Wiberg 8aba63a9e3 Track oldpid of services to fix empty pid in restart message
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-12 00:13:15 +01:00
Joachim Wiberg 3d98ad293b Restore telinit symlink to finit, compat only
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 23:59:39 +01:00
Joachim Wiberg 156e86f395 Revert "Travis-CI: disable clang temporarily for Coverity Scan run"
This reverts commit d859ecd21d.
2021-03-11 22:28:41 +01:00
Joachim Wiberg d859ecd21d Travis-CI: disable clang temporarily for Coverity Scan run
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 22:04:56 +01:00
Joachim Wiberg cbec78dbbc initctl: show pid file and condition for foo in 'status foo' command
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 22:01:42 +01:00
Joachim Wiberg 572291a1d9 Don't guess pid file name, either there's one declared or not
The previous patch just added dynamic tracking of non-declared pid
files, so we no longer need to make stuff up.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 22:00:29 +01:00
Joachim Wiberg 280608f30e plugins: pidfile: track PID file in svc, if svc has none declared
Services that create their own PID files usually don't declare one with
Finit.  This patch adds support to track those PID files anywayt at
runtime for the purpose of identifying match svc_t when a PID file is
removed, i.e. when a service exits.

 - On IN_CREATE the pidfile.so plugin saves the pid file name in svc_t
 - On ON_DELETE the pidfile.so plugin finds svc_t based on pid file

Quicker tracking and less dead code, win-win.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 21:56:49 +01:00
Joachim Wiberg 5c4146cfe4 Export pid_file_set() function, useful to other subsystems as well
Rename, refactor, and export.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 21:52:44 +01:00
Joachim Wiberg 30e3f5a33c Wait only one second at shutdown/reboot for lingering processes
Transitioning to runlevel 0 or 6 handles graceful shutdown of any
managed run/task/sysv/services.  So we can replace the old 2 sec delay
with a shorter one for any non-managed still lingering process.

Also, some minor cleanup.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 21:48:49 +01:00
Joachim Wiberg 2c12379a30 Only reload .conf files when not going to shutdown or reboot
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 21:47:22 +01:00
Joachim Wiberg 5f026c8149 initctl: when svc has status missing, highlight what is missing
To aid with debugging, highlight if env file, binary, or both are the
missing component(s) and causing status 'missing'.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 15:58:36 +01:00
Joachim Wiberg 90005235e8 initctl: minor, rename local variable
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 15:11:16 +01:00
Joachim Wiberg 6b688c4211 Refactor, share env helper fns with initctl
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 15:10:10 +01:00
Joachim Wiberg bdf5f8fc90 initctl: show halted state status reason in bold everywhere
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 14:35:09 +01:00
Joachim Wiberg 79f7a8ccfb Reclaim 900 msec in boot time from final_worker() at bootstrap
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 14:03:15 +01:00
Joachim Wiberg 3b19db04e3 initctl: add cond, user, group and visual hints for status command
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 12:52:05 +01:00
Joachim Wiberg 93402b81d7 initctl: disable ctrl chars also in runlevel_string() when -p
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 12:51:23 +01:00
Joachim Wiberg 6dec4c9ca0 Set default user/group for services that don't specify any
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 12:49:14 +01:00
Joachim Wiberg 548acf27cf Minor refactor, rename RESPAWN_MAX -> SVC_RESPAWN_MAX
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 12:48:44 +01:00
Joachim Wiberg cd0bc57ca8 Clear stale PID conditions when a service stops
Protect against corner cases where pid conditions are not cleaned up.
We don't want such conditions to remain asserted when the process has
terminated.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 11:44:23 +01:00
Joachim Wiberg 11a3318c02 Properly set status 'missing' for missing binaries and/or env files
When we try to start a service/run/task we call whichp() to see if the
binary exists, either tha absolute path given in the .conf file, or in
the $PATH we run with.  If binary, or the env: file, doesn't exist we
now set svc_missing() state.

On `initctl reload` we unblock the service to be able to check again.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 11:44:23 +01:00
Joachim Wiberg 2711b27971 plugins: pidfile: tricky zebra doesn't close its pid file
This patch fixes a few really hard problems wrt PID files:

 1. Listening for IN_CREATE events means we get notified immediately by
    the kernel when someone calls open()/fopen() on a PID file.  Reading
    the contents returns 0, thank you atoi() ... so we drop IN_CREATE
    and instead look for IN_CLOSE_WRITE, there fixed it!  Not quite ...

 2. Some programs, like Zebra, and other Quagga/Frr daemons, don't
    close() their PID files after creation.  Instead they ftruncate()
    and keep them open, and locked.  Presumably to get a mechanism to
    detect already running instances -- messes life up a bit for the
    rest of us though.  So we need to read files after IN_MODIFY too.

 3. We also want to track IN_DELETE so we can deassert conditions when
    services exit gracefully and clean up their PID files

The rest fo the commit is debug instrumentation changes.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 11:44:23 +01:00
Joachim Wiberg 05ac9c18c9 Update changelog, new inictcl cond set|clear commands
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 11:44:23 +01:00
Joachim Wiberg 1a7092f545 doc: update with new 'initctl cond set|clear foo' commands
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 11:44:23 +01:00
Joachim Wiberg 8000d361b3 initctl: restore 'cond [set|clr] foo' for user-defined conditions
In 7447192 we dropped support for 'cond set' and 'cond clear' commands
with the motivation they were unsafe and sent the wrong message to the
user.  That was true, but mostly because it was too generic and required
the user to call `initctl reload` to apply the changes.

This patch restores the behavior, albeit in a very reduced and simple
format.  All conditions set with this command are constrained to the
'usr/...' namespace.  No subdirectories are allowed.  The argument to
the 'cond set|clear' command is disallowed if it contains '/' or '.'
but anything else is supported, for example:

    initctl cond set foo:2

creates a static/oneshot condition in /run/finit/cond/usr/foo:2

These conditions are static and are fully handled by the user.  The
initctl command is the recommended, and only supported, way of setting
and clearing usr conditions.

This patch also includes a new plugin, usr.so, which is a very simple
inotify plugin for the /run/finit/cond/usr/ directory.  When files are
created or removed here the plugin tells the Finit condition engine to
update and trigger service changes.

For instance, the following service is not started by default at boot:

    service <usr/foo> myservice -- MyService

However, as soon as `initctl cond set foo` is called, myservice starts.
Consequently, it is stopped when `initctl cond clr foo` is called.

Another major difference from the original is that this implementation
doesn't send IPC commands to create/delete the conditions.  This makes
calling these new commands non-blocking so they can be used very early
in the bootstrap, by plugins, if needed.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 11:44:23 +01:00
Joachim Wiberg e3c8febe3b Refactor; common nomenclature, ordering, possib. security fix
- Use _PATH_foo for all condition paths, *with* trailing /
- Read condition file first, may not exist, in which case we save time
- Change from libte makepath() to mkpath(), this changes from hard-coded
  0777 perms on all cond dirs to 0755 -- possible security fix

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 11:44:23 +01:00
Joachim Wiberg 32ca117949 initctl: refactor command line parser, use concept from mroutectl
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-11 11:44:23 +01:00
Joachim Wiberg b966d031c8 initctl: enable plain mode for ls and utmp commands
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-09 21:30:11 +01:00
Joachim Wiberg 905c2678a3 Fix path to fallback console and handle leading /dev from cmdline
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-08 09:33:16 +01:00
Joachim Wiberg 7d429bdbb1 initctl: restore -p,--plain output for headings and ps listing
This is the same output style as used in the mroutectl and pimctl tools.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 23:30:13 +01:00
Joachim Wiberg ccfea59cb3 Bump version for Finit v4.0-rc2 release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 18:50:50 +01:00
Joachim Wiberg 7b315364ca Update initctl help text, and output, relocate screenshot
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 15:38:23 +01:00
Joachim Wiberg e9e28ece1c Add Finit4 + Alpine screenshot
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 15:28:32 +01:00
Joachim Wiberg c1f9b4e1f6 Use .svg for Travis-CI status badge
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 15:17:26 +01:00
Joachim Wiberg 6ebb23e0f0 Travis-CI: Minor, adjust configure line for Coverity
- Drop commented-out sudo:true
- Drop unnecessary configure option

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 15:16:26 +01:00
Joachim Wiberg 026c229005 Revert "Travis-CI: disable clang and unit tests for Coverity Scan run"
This reverts commit c71bafd495.
2021-03-07 15:14:47 +01:00
Joachim Wiberg 4d05bf9359 Mark affected services as dirty if their rdeps are dirty
Provided a configuration that looks like this:

ospfd.conf:
    service [2345] <!pid/zebra> log ospfd -A 127.0.0.1 -u root -g root -- OSPF daemon

zebra.conf:
    service [2345] <!> log zebra -A 127.0.0.1 -u root -g root -- Zebra Routing daemon

If zebra.conf is changed, we restart it when `initctl reload` is issued.

This change ensures that ospfd is also restarted, because ospfd depends
on zebra, we must restart it too.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 14:52:19 +01:00
Joachim Wiberg 289247dab9 plugins: netlink: slightly stricter ifname validation
Check if ifname contains double periods, this should *not* be a valid
name, though eth0.10 is, et0..10 is not.  Should protect better against
directory traversal attacks.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 13:25:40 +01:00
Joachim Wiberg e44dfde54e plugins: tty: validate against read() length, not buffer size
Fix ev->len validation; must check against sz read(), not total buffer
size.  Also, fix off-by-one in comparison.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 12:55:12 +01:00
Joachim Wiberg 35b200d3a9 Validate inotify event against read() length, not buffer size
Fix ev->len validation; must check against sz read(), not total buffer
size.  Also, fix off-by-one in comparison.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 12:55:12 +01:00
Joachim Wiberg 5badf4d376 plugins: pidfile: validate against read() length, not buffer size
Fix ev->len validation; must check against sz read(), not total buffer
size.  Also, fix off-by-one in comparison.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 12:48:42 +01:00
Joachim Wiberg 24f274c126 plugins: netlink: validate interface name
Check for spaces and slashes in interface name to prevent path based
attacks.  Found by Coverity Scan.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 12:23:24 +01:00
Joachim Wiberg 1abd43384b plugins: netlink: minor refactor, collapse for-loop
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 12:20:59 +01:00
Joachim Wiberg 70a1acc210 Highly unlikekly, but chdir() needs to be checked
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 11:40:25 +01:00
Joachim Wiberg 6af0446490 plugins: netlink: fix untrusted loop bound, found by Coverity Scan
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 11:28:39 +01:00
Joachim Wiberg ecaf111a4b plugins: tty: possible out-of-bounds read in inotify_event parser
This is a major refactor to use the same construct as in the pidfile.so
plugin to parse kernel inotify events for when TTYs are added removed
from the system.

Found by Coverity Scan.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 10:58:55 +01:00
Joachim Wiberg 32ec25b070 Check return value from remove(), found by Coverity Scan
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 10:34:23 +01:00
Joachim Wiberg 305ad302f2 Refactor, reduce code duplication
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 10:28:54 +01:00
Joachim Wiberg d6390244ad Fix possible out-of-bounds read in inotify_event parser
Found by Coverity Scan.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 09:57:07 +01:00
Joachim Wiberg 790a316607 Log rotate: improved handling of return values from syscalls
- Align the two implementations in logit.c and utmp-api.c
 - Use libite APIs to handle common constructs
 - If gzip fails, don't remove rotated-to file
 - Don't try gzip if rename fails
 - Issues found by Coverity Scan

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 09:57:07 +01:00
Joachim Wiberg 77aa941e84 Mounting devtmpfs may fail if already mounted
Ignore EBUSY errors, the kernel has the ability to automount /dev as
soon as the rootfs has been mounted.  This may be added to procfs and
sysfs later, so we make this a general change in fs_init().

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-07 09:48:02 +01:00
Joachim Wiberg e0a65f67c9 Minor, staticify, reduce includes and allow debug to kmsg
The loglevel setting should control all logging, regardless of
where we target it.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-06 23:55:54 +01:00
Joachim Wiberg 3ed291789c Fix GCC warnings with _FORTIFY_SOURCE=2
- Decleare some return values with (void)fn(), for cases where
  we don't care (dropping table headers), or best effor
- Check return value from fgets() and chdir() in some cases that
  are valid, i.e., continuing execution is pointless

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-05 12:36:48 +01:00
Joachim Wiberg 774a24cdd8 Allow building with -D_FORTIFY_SOURCE[=1,2]
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-05 11:17:59 +01:00
Joachim Wiberg 6011f91f22 Fix possible out-of-bounds read in inotify_event parser
Found by Coverity Scan.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-05 11:16:37 +01:00
Joachim Wiberg 07a364e570 initctl: fix use of possibly unterminated string, found by Coverity
No point in using rq.data, which could have been modified, instead use
the input argument to the function in the error message.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-05 10:51:25 +01:00
Joachim Wiberg 5b9d99011b Fix long-standing bug in reset of rlimits between reconf
While skimming through the results of the latest Coverity Scan, I
discovered that that the reset logic of global rlimits was broken.
This it seems to have been since its first introduction in Finit.

We fix this by reading initial rlimits at bootstrap, then for each
reconf, including the first, we seed global rlimits with the initial
ones -- thus resetting between each reconf.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-05 10:44:01 +01:00
Joachim Wiberg f4f773b4c7 Initialize fallback svc_t in client comms, found by Coverity Scan
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-05 10:35:12 +01:00
Joachim Wiberg 9d949c4186 Fix possible NULL ptr deref in cmdline option parser
Found by Coverity Scan.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-05 10:21:58 +01:00
Joachim Wiberg 1434561a46 initctl: fix cut-and-paste error in new 'show' command
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-05 10:17:52 +01:00
Joachim Wiberg cf3d3f6f8e Properly check return value from mount(2) and display error message
Found by Coverity Scan.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-05 10:15:46 +01:00
Joachim Wiberg 621da582c2 Fix obvious bug in ismnt() found by Coverity Scan
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-05 10:10:45 +01:00
Joachim Wiberg 9167d9255d Work around weird finding from Coverity Scan
The utmp_set() function allows id==NULL, so default the line to NULL and
avoid parsing empty lines.  Which is better code anyway.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-05 09:59:58 +01:00
Joachim Wiberg c71bafd495 Travis-CI: disable clang and unit tests for Coverity Scan run
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-05 08:25:32 +01:00
Joachim Wiberg b3b7a33252 initctl: developer mode, hidden command
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-05 08:22:38 +01:00
Joachim Wiberg d1efc60c84 initctl: add show command to cat foo.conf, default finit.conf
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-05 08:06:01 +01:00
Joachim Wiberg 9a24dfd98d initctl: refactor command composition for status command
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-05 00:13:01 +01:00
Joachim Wiberg 0e3fe5e3bf initctl: fix too long args list to status command
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-05 00:06:36 +01:00
Joachim Wiberg 224f42c344 cgreaper.sh: drop log message when cleaning up
Mostly used during development, doesn't really provide any value to the
user at normal runtime.  Leave it commented out though so user can do
debug themselves if needed.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-04 23:57:48 +01:00
Joachim Wiberg e60eb94810 initctl: complete rewrite of cgroup dumper in 'ps' command
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-04 23:49:42 +01:00
Joachim Wiberg 464bc831a5 Minor refactor, create FINIT_CGPATH in finit.h to reduce duplicaiton
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-04 23:48:57 +01:00
Joachim Wiberg e84911fe52 cgroup: create 'name' or 'name:id' entries, not 'name:'
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-04 23:46:25 +01:00
Joachim Wiberg 4bcfd606b4 Refactor screen_init(), use methods developed in pimd project
- Refactor screen_init()
  - use native impl. of TTY probing from pimd project
    - check if TIOCWINSZ works
    - check if we're running in watch(1), for initctl
  - check if ANSI goto(999,999) escape seq. works (invasive)
  - fallback to 80x24
- Drop screen_exit()
- Rename screen_init() to get_width(), for now, matching pimd
- Relocate call in main() to banner(), first fn to write to TTY

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-04 23:23:31 +01:00
Joachim Wiberg fe27ed17eb initctl: status command formatting updates, add origin .conf file
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-04 07:27:06 +01:00
Joachim Wiberg b3ea23ed26 initctl: drop old 'job:id' format support, canonical is 'name:id'
With the recent changes to the condition handling it has become more and
more evident that the canonical reference for a task/run/service is the
NAME:ID representation.  Up until now we've kept the older JOB:ID for
some sort of compatibility fallback.

This patch removes the support to simplify maintenance going forward.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-04 07:23:19 +01:00
Joachim Wiberg 439699ef52 configure: improve --with-sysconfig help text slightly
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-03 07:06:17 +01:00
Joachim Wiberg f20936dae0 configure: slight reorder; first enable -> then disable options
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-03 07:05:55 +01:00
Joachim Wiberg d5bea7eeeb Re-concatenate string arguments to commands, ugly patch
This is quite possibly the ugliest pieces of code in this project.

It is a quick fix to the problem with strtok() to split up the cmd into
cmd + args[].  Without it wordexp() will later get very cranky about
trying to expand `"Partial` strings from commands like this:

    /sbin/udhcpc-wrapper.sh -f -S -V "myOS v9.99" -t 8 -T 5 -A 10 -R -p \
        /var/run/udhcpc-vlan1.pid -i vlan1 -x hostname:myos-12-34-50 -o \
	-O 1 -O 3 -O 6 -O 7 -O 12 -O 15 -O 42 -O 121 -a

Yes really.

It's an obviously far from perfect solution.  One might even start to
question why we have an args[64[64] per svc_t and not just a cmd + arg?

Well, there's a lot of improvement potential here, let's talk!

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-02 22:33:41 +01:00
Joachim Wiberg 5da0a460fe initctl: fix runlevel and start/stop/restart segfault regression
Introduced in e2f6ac0.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-02 20:31:34 +01:00
Joachim Wiberg 63033d7dcf Drop developer debug message
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-02 20:23:43 +01:00
Joachim Wiberg 2857dafaf4 plugins: pidfile: Fall back to /run if _PATH_VARRUN doesn't exist
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-02 20:22:56 +01:00
Joachim Wiberg 8355c37285 initctl: fix edit command fallback handling
If mg doesn't exist, command doesn't evaluate the second argument (vi),
so we need to use `... || command -v vi` instead.  Found during demo,
because why not.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-01 17:36:06 +01:00
Joachim Wiberg dc49b523d1 initctl: fix cond segfault, regression introduced in e2f6ac0
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-03-01 11:13:02 +01:00
Magnus Malm bf863673c8 Update list of contributors
Signed-off-by: Magnus Malm <magnusmalm@gmail.com>
2021-03-01 09:26:31 +01:00
54 changed files with 2069 additions and 798 deletions
+2 -5
View File
@@ -5,9 +5,6 @@ language: c
# Use docker for quicker builds, it now allows https://docs.travis-ci.com/user/apt/
sudo: false
# Use non-docker builds atm. to install libuEv and libite from source
#sudo: true
# Test build with both GCC and Clang (LLVM)
compiler:
- gcc
@@ -30,7 +27,7 @@ addons:
name: "troglobit/finit"
description: "finit | Fast & Extensible init for Linux"
notification_email: troglobit@gmail.com
build_command_prepend: "./autogen.sh && ./configure --prefix=/tmp"
build_command_prepend: "./autogen.sh && ./configure --prefix=/tmp --disable-doc --disable-contrib --enable-watchdog --enable-fallback-shell"
build_command: "make"
branch_pattern: dev
@@ -48,7 +45,7 @@ script:
- ./configure --prefix=/tmp --enable-static
- make clean
- make V=1 -j5
- ./configure --prefix=/tmp --enable-x11-common-plugin --enable-fallback-shell --enable-watchdog --enable-logit
- ./configure --prefix=/tmp --enable-x11-common-plugin --enable-fallback-shell --enable-watchdog
- cat config.log
- make clean
- make V=1 -j5
+1
View File
@@ -6,6 +6,7 @@ Major Contributors:
* Mattias Walström <mattias.walstrom@westermo.se>
* Jonas Johansson <jonas.johansson@westermo.se>
* Jacques de Laval <jacques@de-laval.se>
* Magnus Malm <magnusmalm@gmail.com>
Original Author(s):
* Claudio Matsuoka <cmatsuoka@gmail.com>
+4 -1
View File
@@ -4,7 +4,7 @@ Change Log
All relevant changes are documented in this file.
[4.0][UNRELEASED] - 2021-02-21
[4.0][UNRELEASED] - 2021-03-xx
------------------------------
This release became v4.0, and not v3.2, because of incompatible changes
@@ -18,6 +18,9 @@ make sure to read the whole changelog when upgrading.
with a fallback to sending signals as per traditional SysV init. The
`-f` (force) flag remains, where `reboot(2)` is called directly
* Introducing Finit progress 𝓜𝓸𝓭𝓮𝓻𝓷
* The `inictl cond set|clear COND` have changed completely. Constrained
to a flat `<usr/...>` namespace and automatically activated by a new
`usr.so` plugin that checks services for usr condition changes
* Removed built-in inetd super server. If you need this functionality,
use an external inetd, like xinetd, instead. A pull request for a
stand-alone inetd, like watchdogd and getty, is most welcome!
+58 -33
View File
@@ -26,6 +26,9 @@ Finit is a simple alternative to [SysV init][4] and [systemd][6]. It
was reverse engineered from the [EeePC fastinit][] over ten years ago
by [Claudio Matsuoka][] — "gaps filled with frog DNA …"
![Alpine screenshot](img/finit4-screenshot.png)
*Figure 1: Screenshot showing Finit booting [Alpine Linux](https://www.alpinelinux.org/).*
Focus is on small and embedded systems, although Finit is fully usable
on server and desktop systems as well. For working examples, see the
[contrib/](contrib/) section with tutorials for:
@@ -34,9 +37,6 @@ on server and desktop systems as well. For working examples, see the
* [Alpine Linux](contrib/alpine/), and
* [Debian GNU/Linux](contrib/debian/)
![Alpine screenshot](img/alpine-screenshot.png)
The screenshot shows [Alpine Linux](https://www.alpinelinux.org/).
Example
-------
@@ -348,11 +348,16 @@ Finit also implements a modern API to query status, and start/stop
services, called `initctl`. Unlike `telinit` the `initctl` tool does
not return until the given command has fully completed.
```shell
~ $ initctl -h
```
alpine:~# initctl help
Usage: initctl [OPTIONS] [COMMAND]
Options:
-b, --batch Batch mode, no screen size probing
-c, --create Create missing paths (and files) as needed
-f, --force Ignore missing files and arguments, never prompt
-p, --plain Use plain table headings, no ctrl chars
-t, --no-heading Skip table headings
-v, --verbose Verbose output
-h, --help This help text
@@ -360,29 +365,36 @@ Commands:
debug Toggle Finit (daemon) debug
help This help text
version Show Finit version
list List all .conf in /etc/finit.d/
enable <CONF> Enable .conf in /etc/finit.d/available/
disable <CONF> Disable .conf in /etc/finit.d/[enabled/]
touch <CONF> Mark .conf in /etc/finit.d/ for reload
reload Reload *.conf in /etc/finit.d/ (activates changes)
ls | list List all .conf in /etc/finit.d
create <CONF> Create .conf in /etc/finit.d/available
delete <CONF> Delete .conf in /etc/finit.d/available
show <CONF> Show .conf in /etc/finit.d/available
edit <CONF> Edit .conf in /etc/finit.d/available
touch <CONF> Change .conf in /etc/finit.d/available
enable <CONF> Enable .conf in /etc/finit.d/available
disable <CONF> Disable .conf in /etc/finit.d/enabled
reload Reload *.conf in /etc/finit.d (activate changes)
cond set <COND> Set (assert) user-defined condition +usr/COND
cond clear <COND> Clear (deassert) user-defined condition -usr/COND
cond show Show condition status
cond dump Dump all conditions and their status
log [NAME] Show ten last Finit, or NAME, messages from syslog
start <JOB|NAME>[:ID] Start service by job# or name, with optional ID
stop <JOB|NAME>[:ID] Stop/Pause a running service by job# or name
restart <JOB|NAME>[:ID] Restart (stop/start) service by job# or name
status <JOB|NAME>[:ID] Show service status, by job# or name
status | show Show status of services, default command
start <NAME>[:ID] Start service by name, with optional ID
stop <NAME>[:ID] Stop/Pause a running service by name
restart <NAME>[:ID] Restart (stop/start) service by name
status <NAME>[:ID] Show service status, by name
status Show status of services, default command
ps List processes based on cgroups
runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot
reboot Reboot system
halt Halt system
poweroff Halt and power off system
utmp show Raw dump of UTMP/WTMP db
suspend Suspend system
```
For services *not* supporting `SIGHUP` the `<!>` notation in the .conf
@@ -395,18 +407,31 @@ the current runlevel these services will not be respawned automatically
by Finit if they exit (crash). Hence, if the runlevel is 2, the below
Dropbear SSH service will not be restarted if it is killed or exits.
```shell
~ $ initctl status -v
1 running 476 [S12345] /sbin/watchdog -T 16 -t 2 -F /dev/watchdog
2 running 477 [S12345] /sbin/syslogd -n -b 3 -D
3 running 478 [S12345] /sbin/klogd -n
4:1 inetd 0 [2345] internal time allow *:37,!eth0
4:2 inetd 0 [2345] internal time allow eth0:3737
5:1 inetd 0 [2345] /sbin/telnetd allow *:23 deny eth0,eth1
5:2 inetd 0 [2345] /sbin/telnetd allow eth0:2323,eth2:2323,eth1:2323
6:1 inetd 0 [345] /sbin/dropbear allow eth0:222
6:2 inetd 0 [345] /sbin/dropbear allow *:22 deny eth0
```
alpine:~# initctl
PID IDENT STATUS RUNLEVELS DESCRIPTION
======================================================================
1506 acpid running [--2345----] ACPI daemon
1509 crond running [--2345----] Cron daemon
1489 dropbear running [--2345----] Dropbear SSH daemon
1511 klogd running [S12345----] Kernel log daemon
1512 ntpd running [--2345----] NTP daemon
1473 syslogd running [S12345----] Syslog daemon
alpine:~# initctl -v
PID IDENT STATUS RUNLEVELS COMMAND
======================================================================
1506 acpid running [--2345----] acpid -f
1509 crond running [--2345----] crond -f -S $CRON_OPTS
1489 dropbear running [--2345----] dropbear -R -F $DROPBEAR_OPTS
1511 klogd running [S12345----] klogd -n $KLOGD_OPTS
1512 ntpd running [--2345----] ntpd -n $NTPD_OPTS
1473 syslogd running [S12345----] syslogd -n
```
The environment variables to each of the services above are read from,
in the case of Alpine Linux, `/etc/conf.d/`. Other distributions may
have other directories, e.g., Debian use `/etc/default/`.
Requirements
@@ -473,6 +498,6 @@ and proposed extensions.
[License]: https://en.wikipedia.org/wiki/MIT_License
[License Badge]: https://img.shields.io/badge/License-MIT-teal.svg
[Travis]: https://travis-ci.org/troglobit/finit
[Travis Status]: https://travis-ci.org/troglobit/finit.png?branch=master
[Travis Status]: https://travis-ci.org/troglobit/finit.svg?branch=master
[Coverity Scan]: https://scan.coverity.com/projects/3545
[Coverity Status]: https://scan.coverity.com/projects/3545/badge.svg
+6 -4
View File
@@ -1,4 +1,4 @@
AC_INIT(Finit, 4.0-rc1, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/finit.html)
AC_INIT(Finit, 4.0-rc3, https://github.com/troglobit/finit/issues, finit, https://troglobit.com/finit.html)
AC_CONFIG_AUX_DIR(aux)
AM_INIT_AUTOMAKE([1.11 foreign dist-xz subdir-objects])
AM_SILENT_RULES([yes])
@@ -32,7 +32,7 @@ AC_PROG_MKDIR_P
# Configuration.
AC_HEADER_STDC
AC_CHECK_HEADERS([fstaby.h termios.h])
AC_CHECK_HEADERS([fstaby.h termios.h sys/ioctl.h])
AC_CHECK_FUNCS([strstr getopt getfsenty])
# Check for uint[8,16,32]_t
@@ -89,11 +89,11 @@ AC_ARG_ENABLE(contrib,
enable_all_plugins=auto
AC_PLUGIN([alsa-utils], [no], [Save and restore ALSA sound settings using alsactl])
AC_PLUGIN([dbus], [no], [Setup and start system message bus, D-Bus])
AC_PLUGIN([hotplug], [yes], [Setup and start udev or mdev hotplug daemon])
AC_PLUGIN([modules-load], [no], [Scans /etc/modules-load.d for modules to load])
AC_PLUGIN([resolvconf], [no], [Setup necessary files for resolvconf])
AC_PLUGIN([x11-common], [no], [Console setup (for X)])
AC_PLUGIN([netlink], [yes], [Basic netlink plugin for IFUP/IFDN and GW events. Can be replaced with externally built plugin that links with libnl or similar.])
AC_PLUGIN([hotplug], [yes], [Setup and start udev or mdev hotplug daemon])
# Check for extra arguments or packages
AC_ARG_WITH(heading,
@@ -113,7 +113,7 @@ AC_ARG_WITH(rc-local,
[rclocal=$withval], [rclocal=yes])
AC_ARG_WITH(sysconfig,
AS_HELP_STRING([--with-sysconfig=DIR], [System environment, default /etc/default & /etc/conf.d]),
AS_HELP_STRING([--with-sysconfig=DIR], [System environment directory, sourced by .conf files, default /etc/default & /etc/conf.d]),
[sysconfig=$withval], [sysconfig=default])
AC_ARG_WITH(hostname,
@@ -153,6 +153,8 @@ AS_IF([test "x$enable_logit" != "xno"], [
AS_IF([test "x$enable_logrotate" != "xno"], [
AC_DEFINE(LOGROTATE_ENABLED, 1, [Enable built-in rotation of /var/log/wtmp et al.])])
AM_CONDITIONAL(LOGROTATE, [test "x$enable_logrotate" = "xyes"])
### With features ##############################################################################
AS_IF([test "x$with_config" != "xno"], [
AS_IF([test "x$conf" = "xyes"], [
+27 -1
View File
@@ -45,7 +45,31 @@ being created.
Triggering
----------
Conditions can only be triggered by built-in plugins.
Conditions are mainly triggered (asserted) by built-in plugins, e.g.,
`netlink.so` and `pidfile.so`, see below for built-in conditions. Finit
also supports user-defined conditions, sometimes referred to as static
or one-shot conditions. They live in the `<usr/...` namespace and are
constrained to a flat hierarchy without any sub-directories, unlike the
pidfile plugin, for instance.
User-defined conditions are controlled using the `initctl cond set` and
`initctl cond clear` commands:
initctl cond set foo
initctl cond clear foo
The purpose of user-defined conditions is to be able to start or stop
services, or run/task jobs, on external site-dependent stimuli.
Example:
service [2345] <usr/foo> alarm --arg foo -- Foo alarm
For convenience, prefixing with `usr/` is allowed, but any other slashes
or period characters are disallowed. E.g., to trigger the `Foo alarm`,
the same as above, can also be achieved like this:
initctl cond set usr/foo
initctl cond clear usr/foo
Conditions retain their current state until the next reconfiguration or
runlevel change. At that point all set conditions transition into the
@@ -57,6 +81,8 @@ unchanged) state of it.
> **Note:** For `pid/` conditions it is expected that services "touch"
> or recreate their PID file on `SIGHUP`.
Static (one-shot) conditions, like `usr/`, never enter the `flux` state.
Built-in Conditions
-------------------
-3
View File
@@ -483,9 +483,6 @@ Log rotation is controlled using the global `log` setting.
service log:prio:user.warn,tag:ntpd /sbin/ntpd pool.ntp.org -- NTP daemon
Worth noting is that conditions is allowed for all these stanzas. For a
detailed description, see the [Conditions](conditions.md) document.
Limitations
-----------
+1 -1
View File
@@ -18,7 +18,7 @@ The current state depends on the two following conditions:
A service may be stopped, or blocked, for several reasons:
- The user has manually stopped the service using `initctl stop JOB`
- The user has manually stopped the service using `initctl stop NAME`
- The program exits immediately. I.e. keeps crashing (make sure to use
the 'run this service in the foreground' command line option)
- The binary is missing in the filesystem
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

+2 -2
View File
@@ -7,7 +7,7 @@ AM_CPPFLAGS += $(lite_CFLAGS)
if STATIC
noinst_LTLIBRARIES = libplug.la
libplug_la_SOURCES = bootmisc.c modprobe.c rtc.c pidfile.c procps.c tty.c urandom.c
libplug_la_SOURCES = bootmisc.c modprobe.c rtc.c pidfile.c procps.c tty.c urandom.c usr.c
if BUILD_ALSA_UTILS_PLUGIN
libplug_la_SOURCES += alsa-utils.c
@@ -34,7 +34,7 @@ libplug_la_SOURCES += resolvconf.c
endif
else
pkglib_LTLIBRARIES = bootmisc.la modprobe.la rtc.la pidfile.la procps.la tty.la urandom.la
pkglib_LTLIBRARIES = bootmisc.la modprobe.la rtc.la pidfile.la procps.la tty.la urandom.la usr.la
if BUILD_ALSA_UTILS_PLUGIN
pkglib_LTLIBRARIES += alsa-utils.la
+20 -30
View File
@@ -99,13 +99,14 @@ static void bootclean(void)
*/
static void setup(void *arg)
{
int uid, gid;
mode_t prev;
/* Cleanup stale files, if any still linger on. */
bootclean();
prev = umask(0);
_d("Setting up FHS structure in /var ...");
umask(0);
makedir("/var/cache", 0755);
makedir("/var/db", 0755); /* _PATH_VARDB on some systems */
makedir("/var/games", 0755);
@@ -133,19 +134,21 @@ static void setup(void *arg)
makedir("/var/tmp", 0755);
makedir("/var/empty", 0755);
/*
* If /etc/group or "utmp" group is missing,
* default to "root"/"wheel" group
*/
_d("Setting up necessary UTMP files ...");
gid = getgroup("utmp");
if (gid < 0)
gid = 0;
/*
* UTMP actually needs multiple db files
*/
if (has_utmp()) {
int gid;
/*
* If /etc/group or "utmp" group is missing, default to
* "root", or "wheel", group.
*/
_d("Setting up necessary UTMP files ...");
gid = getgroup("utmp");
if (gid < 0)
gid = 0;
create("/var/run/utmp", 0644, 0, gid); /* Currently logged in */
create("/var/log/wtmp", 0644, 0, gid); /* Login history */
create("/var/log/btmp", 0600, 0, gid); /* Failed logins */
@@ -165,32 +168,19 @@ static void setup(void *arg)
makedir("/var/run/network",0755); /* Needed by Debian/Ubuntu ifupdown */
makedir("/var/run/lldpd", 0755); /* Needed by lldpd */
makedir("/var/run/pluto", 0755); /* Needed by Openswan */
makedir("/var/run/quagga", 0755); /* Needed by Quagga */
makedir("/var/log/quagga", 0755); /* Needed by Quagga */
makedir("/var/run/frr", 0755); /* Needed by FRR */
makedir("/var/log/frr", 0755); /* Needed by FRR */
mksubsys("/var/run/quagga", 0755, "quagga", "quagga");
mksubsys("/var/log/quagga", 0755, "quagga", "quagga");
mksubsys("/var/run/frr", 0755, "frr", "frr");
mksubsys("/var/log/frr", 0755, "frr", "frr");
makedir("/var/run/sshd", 01755); /* OpenSSH */
gid = getgroup("tty");
if (gid < 0)
gid = 0;
makefifo("/dev/xconsole", 0640); /* sysklogd */
chown("/dev/xconsole", 0, gid);
if (!fexist("/etc/mtab"))
symlink("../proc/self/mounts", "/etc/mtab");
/* Void Linux has a uuidd that runs as uuid:uuid and needs /run/uuid */
uid = getuser("uuidd", NULL);
if (uid >= 0) {
gid = getgroup("uuidd");
if (gid < 0)
gid = 0;
makedir("/var/run/uuidd", 0755);
chown("/var/run/uuidd", uid, gid);
}
mksubsys("/var/run/uuidd", 0755, "uuidd", "uuidd");
umask(022);
umask(prev);
}
static plugin_t plugin = {
+12 -10
View File
@@ -32,13 +32,16 @@
#include "conf.h"
#define DAEMON "dbus-daemon"
#define ARGS "--nofork --system"
#define ARGS "--nofork --system --syslog-only"
#define DESC "D-Bus message bus daemon"
#define DAEMONUSER "messagebus"
static void setup(void *arg)
{
char *cmd;
char line[256];
mode_t prev;
char *cmd;
cmd = which(DAEMON);
if (!cmd) {
@@ -46,24 +49,23 @@ static void setup(void *arg)
return;
}
umask(0);
prev =umask(0);
makedir("/var/run/dbus", 0755);
makedir("/var/lock/subsys", 0755);
makedir("/var/lock/subsys/messagebus", 0755);
mksubsys("/var/run/dbus", 0755, DAEMONUSER, DAEMONUSER);
if (whichp("dbus-uuidgen"))
run("dbus-uuidgen --ensure");
run_interactive("dbus-uuidgen --ensure", "Creating machine UUID for D-Bus");
/* Clean up from any previous pre-bootstrap run */
erase("/var/run/dbus/pid");
remove("/var/run/dbus/pid");
/* Register service with Finit */
snprintf(line, sizeof(line), "[S12345] pid:!/run/dbus/pid log:prio:daemon.info,tag:dbus %s %s -- %s", cmd, ARGS, DESC);
snprintf(line, sizeof(line), "[S12345] @%s:%s %s %s -- %s",
DAEMONUSER, DAEMONUSER, cmd, ARGS, DESC);
if (service_register(SVC_TYPE_SERVICE, line, global_rlimit, NULL))
_pe("Failed registering %s", DAEMON);
free(cmd);
umask(022);
umask(prev);
}
static plugin_t plugin = {
+75 -40
View File
@@ -17,6 +17,7 @@
*/
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <net/if.h> /* IFNAMSIZ */
#include <sys/socket.h>
@@ -48,7 +49,7 @@ static int nlmsg_validate(struct nlmsghdr *nh, size_t len)
return 0;
}
static void nl_route(struct nlmsghdr *nlmsg)
static void nl_route(struct nlmsghdr *nlmsg, ssize_t len)
{
struct rtmsg *r;
struct rtattr *a;
@@ -63,8 +64,14 @@ static void nl_route(struct nlmsghdr *nlmsg)
r = NLMSG_DATA(nlmsg);
a = RTM_RTA(r);
la = RTM_PAYLOAD(nlmsg);
if (la >= len) {
_e("Packet too large!");
return;
}
while (RTA_OK(a, la)) {
void *data = RTA_DATA(a);
switch (a->rta_type) {
case RTA_GATEWAY:
gw = *((int *)data);
@@ -105,7 +112,27 @@ static void net_cond_set(char *ifname, char *cond, int set)
cond_clear(msg);
}
static void nl_link(struct nlmsghdr *nlmsg)
static int validate_ifname(const char *ifname)
{
if (!ifname || !ifname[0])
return 1;
if (strlen(ifname) >= IFNAMSIZ)
return 1;
if (strstr(ifname, ".."))
return 1;
while (*ifname) {
if (*ifname == '/' || isspace(*ifname))
return 1;
ifname++;
}
return 0;
}
static void nl_link(struct nlmsghdr *nlmsg, ssize_t len)
{
int la;
char ifname[IFNAMSIZ + 1];
@@ -120,53 +147,61 @@ static void nl_link(struct nlmsghdr *nlmsg)
i = NLMSG_DATA(nlmsg);
a = (struct rtattr *)((char *)i + NLMSG_ALIGN(sizeof(struct ifinfomsg)));
la = NLMSG_PAYLOAD(nlmsg, sizeof(struct ifinfomsg));
if (la >= len) {
_e("Packet too large!");
return;
}
while (RTA_OK(a, la)) {
if (a->rta_type == IFLA_IFNAME) {
strlcpy(ifname, RTA_DATA(a), sizeof(ifname));
switch (nlmsg->nlmsg_type) {
case RTM_NEWLINK:
/*
* New interface has appeared, or interface flags has changed.
* Check ifi_flags here to see if the interface is UP/DOWN
*/
_d("%s: New link, flags 0x%x, change 0x%x", ifname, i->ifi_flags, i->ifi_change);
net_cond_set(ifname, "exist", 1);
net_cond_set(ifname, "up", i->ifi_flags & IFF_UP);
net_cond_set(ifname, "running", i->ifi_flags & IFF_RUNNING);
break;
for (; RTA_OK(a, la); a = RTA_NEXT(a, la)) {
if (a->rta_type != IFLA_IFNAME)
continue;
case RTM_DELLINK:
/* NOTE: Interface has disappeared, not link down ... */
_d("%s: Delete link", ifname);
net_cond_set(ifname, "exist", 0);
net_cond_set(ifname, "up", 0);
net_cond_set(ifname, "running", 0);
break;
case RTM_NEWADDR:
_d("%s: New Address", ifname);
break;
case RTM_DELADDR:
_d("%s: Deconfig Address", ifname);
break;
default:
_d("%s: Msg 0x%x", ifname, nlmsg->nlmsg_type);
break;
}
strlcpy(ifname, RTA_DATA(a), sizeof(ifname));
if (validate_ifname(ifname)) {
_d("Invalid interface name '%s', skipping ...", ifname);
continue;
}
a = RTA_NEXT(a, la);
switch (nlmsg->nlmsg_type) {
case RTM_NEWLINK:
/*
* New interface has appeared, or interface flags has changed.
* Check ifi_flags here to see if the interface is UP/DOWN
*/
_d("%s: New link, flags 0x%x, change 0x%x", ifname, i->ifi_flags, i->ifi_change);
net_cond_set(ifname, "exist", 1);
net_cond_set(ifname, "up", i->ifi_flags & IFF_UP);
net_cond_set(ifname, "running", i->ifi_flags & IFF_RUNNING);
break;
case RTM_DELLINK:
/* NOTE: Interface has disappeared, not link down ... */
_d("%s: Delete link", ifname);
net_cond_set(ifname, "exist", 0);
net_cond_set(ifname, "up", 0);
net_cond_set(ifname, "running", 0);
break;
case RTM_NEWADDR:
_d("%s: New Address", ifname);
break;
case RTM_DELADDR:
_d("%s: Deconfig Address", ifname);
break;
default:
_d("%s: Msg 0x%x", ifname, nlmsg->nlmsg_type);
break;
}
}
}
static void nl_callback(void *arg, int sd, int events)
{
ssize_t len;
static char buf[4096];
struct nlmsghdr *nh;
ssize_t len;
memset(buf, 0, sizeof(buf));
len = recv(sd, buf, sizeof(buf), 0);
@@ -179,9 +214,9 @@ static void nl_callback(void *arg, int sd, int events)
for (nh = (struct nlmsghdr *)buf; !nlmsg_validate(nh, len); nh = NLMSG_NEXT(nh, len)) {
//_d("Well formed netlink message received. type %d ...", nh->nlmsg_type);
if (nh->nlmsg_type == RTM_NEWROUTE || nh->nlmsg_type == RTM_DELROUTE)
nl_route(nh);
nl_route(nh, len);
else
nl_link(nh);
nl_link(nh, len);
}
}
+28 -19
View File
@@ -58,7 +58,7 @@ static int pidfile_add_path(struct iwatch *iw, char *path)
}
}
return iwatch_add(iw, path, IN_ONLYDIR);
return iwatch_add(iw, path, IN_ONLYDIR | IN_CLOSE_WRITE);
}
static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
@@ -68,11 +68,11 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
svc_t *svc;
paste(fn, sizeof(fn), dir, name);
_d("path: %s, mask: %08x", fn, mask);
if (fnmatch("*\\.pid", fn, 0) && fnmatch("*/pid", fn, 0))
return;
_d("path: %s, mask: %08x", fn, mask);
svc = svc_find_by_pidfile(fn);
if (!svc) {
_d("No matching svc for %s", fn);
@@ -82,15 +82,20 @@ static void pidfile_update_conds(char *dir, char *name, uint32_t mask)
_d("Found svc %s for %s with pid %d", svc->name, fn, svc->pid);
mkcond(svc, cond, sizeof(cond));
if (mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) {
if (mask & (IN_CLOSE_WRITE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO)) {
svc_started(svc);
if (!svc_has_pidfile(svc))
pid_file_set(svc, fn, 1);
if (svc_is_forking(svc)) {
pid_t pid;
pid = pid_file_read(pid_file(svc));
_d("Forking service %s changed PID from %d to %d",
svc->cmd, svc->pid, pid);
svc->pid = pid;
if (pid != svc->pid) {
_d("Forking service %s changed PID from %d to %d",
svc->cmd, svc->pid, pid);
svc->pid = pid;
}
}
cond_set(cond);
@@ -151,7 +156,8 @@ static void pidfile_callback(void *arg, int fd, int events)
{
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
struct inotify_event *ev;
ssize_t sz, off;
ssize_t sz;
size_t off;
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) {
@@ -160,15 +166,19 @@ static void pidfile_callback(void *arg, int fd, int events)
}
ev_buf[sz] = 0;
for (off = 0; off < sz; off += sizeof(*ev) + ev->len) {
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
struct iwatch_path *iwp;
if (off + sizeof(*ev) > (size_t)sz)
break;
ev = (struct inotify_event *)&ev_buf[off];
if (off + sizeof(*ev) + ev->len > (size_t)sz)
break;
if (!ev->mask)
continue;
_d("name %s, event: 0x%08x", ev->name, ev->mask);
/* Find base path for this event */
iwp = iwatch_find_by_wd(&iw_pidfile, ev->wd);
if (!iwp)
@@ -179,12 +189,7 @@ static void pidfile_callback(void *arg, int fd, int events)
continue;
}
if (ev->mask & IN_DELETE) {
_d("pidfile %s/%s removed ...", iwp->path, ev->name);
continue;
}
if (ev->mask & (IN_CREATE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO))
if (ev->mask & (IN_CLOSE_WRITE | IN_DELETE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO))
pidfile_update_conds(iwp->path, ev->name, ev->mask);
}
}
@@ -235,8 +240,12 @@ static void pidfile_init(void *arg)
*/
path = realpath(_PATH_VARRUN, NULL);
if (!path) {
_pe("Failed ");
return;
_d("Failed querying realpath(%s): %s", _PATH_VARRUN, strerror(errno));
path = realpath("/run", NULL);
if (!path) {
_e("System does not have %s or /run, aborting.", _PATH_VARRUN);
return;
}
}
if (pidfile_add_path(&iw_pidfile, path))
+36 -21
View File
@@ -53,33 +53,48 @@ static void setup(void)
_pe("Failed starting TTY watcher");
}
static void do_tty(char *tty, size_t len, int creat)
{
char name[len + 6];
struct tty *entry;
snprintf(name, sizeof(name), "/dev/%s", tty);
entry = tty_find(name);
if (entry && tty_enabled(entry)) {
if (creat)
tty_start(entry);
else if (entry->pid)
tty_stop(entry);
}
}
static void watcher(void *arg, int fd, int events)
{
char buf[EVENT_SIZE];
int len = 0;
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
struct inotify_event *ev;
ssize_t sz;
size_t off;
while ((len = read(fd, buf, sizeof(buf)))) {
struct inotify_event *notified = (struct inotify_event *)buf;
char name[notified->len + 6];
struct tty *entry;
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) {
_pe("invalid inotify event");
return;
}
ev_buf[sz] = 0;
if (-1 == len) {
if (errno == EINVAL)
setup();
if (errno == EINTR)
continue;
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
if (off + sizeof(*ev) > (size_t)sz)
break;
break; /* Likely EAGAIN */
}
ev = (struct inotify_event *)&ev_buf[off];
if (off + sizeof(*ev) + ev->len > (size_t)sz)
break;
snprintf(name, sizeof(name), "/dev/%s", notified->name);
entry = tty_find(name);
if (entry && tty_enabled(entry)) {
if (notified->mask & IN_CREATE)
tty_start(entry);
else if (entry->pid)
tty_stop(entry);
}
if (!ev->mask)
continue;
_d("tty %s, event: 0x%08x", ev->name, ev->mask);
do_tty(ev->name, ev->len, ev->mask & IN_CREATE);
}
}
+9 -4
View File
@@ -37,10 +37,11 @@ static void setup(void *arg)
#ifdef RANDOMSEED
if (!fexist(RANDOMSEED)) {
int ret = 1;
mode_t prev;
FILE *fp;
print_desc("Bootstrapping random seed", NULL);
umask(077);
prev = umask(077);
fp = fopen(RANDOMSEED, "w");
if (fp) {
int iter = 128;
@@ -57,7 +58,7 @@ static void setup(void *arg)
ret = fclose(fp);
}
print_result(ret);
umask(0);
umask(prev);
}
print_desc("Initializing random number generator", NULL);
@@ -68,10 +69,14 @@ static void setup(void *arg)
static void save(void *arg)
{
#ifdef RANDOMSEED
umask(077);
mode_t prev;
prev = umask(077);
print_desc("Saving random seed", NULL);
print_result(512 != copyfile("/dev/urandom", RANDOMSEED, 512, 0));
umask(0);
umask(prev);
#endif
}
+133
View File
@@ -0,0 +1,133 @@
/* User-defined condtion event monitor for the Finit condition engine
*
* Copyright (c) 2021 Joachim Wiberg <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <fnmatch.h>
#include <glob.h>
#include <limits.h>
#include <paths.h>
#include "finit.h"
#include "cond.h"
#include "pid.h"
#include "plugin.h"
#include "iwatch.h"
static struct iwatch iw_usr;
static void usr_cond(char *name, uint32_t mask)
{
char cond[MAX_COND_LEN] = COND_USR;
strlcat(cond, name, sizeof(cond));
cond_update(cond);
}
static void usr_callback(void *arg, int fd, int events)
{
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
struct inotify_event *ev;
ssize_t sz;
size_t off;
sz = read(fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) {
_pe("invalid inotify event");
return;
}
ev_buf[sz] = 0;
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
if (off + sizeof(*ev) > (size_t)sz)
break;
ev = (struct inotify_event *)&ev_buf[off];
if (off + sizeof(*ev) + ev->len > (size_t)sz)
break;
_d("name %s, event: 0x%08x", ev->name, ev->mask);
if (!ev->mask)
continue;
if (ev->mask & IN_ISDIR)
continue; /* unsupported */
if (ev->mask & IN_DELETE)
sync(); /* dont ask */
usr_cond(ev->name, ev->mask);
}
}
static void usr_init(void *arg)
{
char usrdir[MAX_ARG_LEN];
char *path;
if (mkpath(pid_runpath(_PATH_CONDUSR, usrdir, sizeof(usrdir)), 0755) && errno != EEXIST) {
_pe("Failed creating %s condition directory, %s", COND_USR, _PATH_CONDUSR);
return;
}
path = realpath(_PATH_CONDUSR, NULL);
if (!path) {
_pe("Cannot figure out real path to %s, aborting", _PATH_CONDUSR);
return;
}
if (iwatch_add(&iw_usr, path, IN_ONLYDIR))
iwatch_exit(&iw_usr);
}
static plugin_t plugin = {
.name = __FILE__,
.hook[HOOK_BASEFS_UP] = { .cb = usr_init },
};
PLUGIN_INIT(plugin_init)
{
int fd;
fd = iwatch_init(&iw_usr);
if (fd < 0)
return;
plugin.io.fd = fd;
plugin.io.cb = usr_callback;
plugin.io.flags = PLUGIN_IO_READ;
plugin_register(&plugin);
}
PLUGIN_EXIT(plugin_exit)
{
iwatch_exit(&iw_usr);
plugin_unregister(&plugin);
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+5 -4
View File
@@ -32,13 +32,14 @@
static void setup(void *arg)
{
const char *username = "nobody";
char line[LINE_SIZE];
mode_t prev;
#ifdef PAM_CONSOLE
int fd;
#endif
char line[LINE_SIZE];
const char *username = "nobody";
umask(0);
prev = umask(0);
makedir("/var/run/console", 01777);
snprintf(line, sizeof(line), "/var/run/console/%s", username);
@@ -58,7 +59,7 @@ static void setup(void *arg)
if (whichp("restorecon"))
run("restorecon /tmp/.ICE-unix /tmp/.X11-unix");
umask(022);
umask(prev);
}
static plugin_t plugin = {
+12 -7
View File
@@ -1,6 +1,5 @@
EXTRA_DIST = cgreaper.sh rescue.conf sample.conf
AM_CPPFLAGS = -U_FORTIFY_SOURCE
AM_CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
AM_CPPFLAGS = -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
if STATIC
AM_CPPFLAGS += -DENABLE_STATIC
AM_LDFLAGS = -static-libtool-libs
@@ -20,8 +19,10 @@ endif
if LOGIT
pkglibexec_PROGRAMS = logit
logit_SOURCES = logit.c
logit_SOURCES = logit.c logrotate.c
logit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
logit_CFLAGS += $(lite_CFLAGS)
logit_LDADD = $(lite_LIBS)
endif
finit_SOURCES = api.c cgroup.c cgroup.h \
@@ -43,6 +44,10 @@ finit_SOURCES = api.c cgroup.c cgroup.h \
tty.c tty.h \
util.c util.h \
utmp-api.c utmp-api.h
if LOGROTATE
finit_SOURCES += logrotate.c
endif
pkginclude_HEADERS = cond.h finit.h helpers.h log.h plugin.h svc.h
finit_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
@@ -54,14 +59,14 @@ else
finit_LDADD += -ldl
endif
initctl_SOURCES = initctl.c client.c client.h \
reboot.c serv.c serv.h svc.h \
cond.c cond.h util.c util.h
initctl_SOURCES = initctl.c initctl.h cgutil.c cgutil.h \
client.c client.h cond.c cond.h reboot.c \
serv.c serv.h svc.h util.c util.h
initctl_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
initctl_CFLAGS += $(lite_CFLAGS)
initctl_LDADD = $(lite_LIBS)
INIT_LNKS = init
INIT_LNKS = init telinit
REBOOT_LNKS = reboot shutdown halt poweroff suspend
if WATCHDOGD
+2 -2
View File
@@ -237,7 +237,7 @@ ev_t ev_list[] = {
static void send_svc(int sd, svc_t *svc)
{
svc_t empty;
svc_t empty = { 0 };
size_t len;
if (!svc) {
@@ -381,7 +381,7 @@ static void api_cb(uev_t *w, void *arg, int events)
break;
case INIT_CMD_SVC_ITER:
_d("svc iter, first: %d", rq.runlevel);
// _d("svc iter, first: %d", rq.runlevel);
/*
* XXX: This severly limits the number of
* simultaneous client connections, but will
+3 -3
View File
@@ -2,10 +2,10 @@
# Called by kernel when last child in cgroup exists
BASE=/sys/fs/cgroup/finit
PROC=`basename $1`
PROC=$(basename "$1")
DIR="$BASE$1"
/libexec/finit/logit -p daemon.info -t finit "$PROC stopped, cleaning up control group $DIR"
rmdir $DIR
#/libexec/finit/logit -p daemon.info -t finit "$PROC stopped, cleaning up control group $DIR"
rmdir "$DIR"
exit 0
+93 -27
View File
@@ -26,20 +26,65 @@
#include <string.h>
#include <lite/lite.h>
#include <sys/mount.h>
#include <sys/sysinfo.h> /* get_nprocs_conf() */
#include "finit.h"
#include "log.h"
#include "util.h"
static int cg_init = 0;
static char controllers[256] = "none";
static void group_init(char *path, char *nproc, int share)
{
char file[strlen(path) + 64];
paste(file, sizeof(file), path, "cgroup.clone_children");
echo(file, 0, "1");
paste(file, sizeof(file), path, "cpuset.cpus");
echo(file, 0, nproc);
paste(file, sizeof(file), path, "cpuset.mems");
echo(file, 0, "0");
paste(file, sizeof(file), path, "cpu.shares");
echo(file, 0, "%d", share);
paste(file, sizeof(file), path, "memory.use_hierarchy");
echo(file, 0, "1");
}
static void append_ctrl(char *ctrl)
{
char *wanted[] = {
"cpu",
"cpuacct",
"cpuset",
"memory"
};
size_t i;
for (i = 0; i < NELEMS(wanted); i++) {
if (strcmp(wanted[i], ctrl))
continue;
if (strcmp(controllers, "none"))
strlcat(controllers, ",", sizeof(controllers));
else
strlcpy(controllers, "", sizeof(controllers));
strlcat(controllers, ctrl, sizeof(controllers));
}
}
/*
* Called by Finit at early boot to mount initial cgroups
*/
void cgroup_init(void)
{
FILE *fp;
char buf[80];
int opts = MS_NODEV | MS_NOEXEC | MS_NOSUID;
char buf[80];
FILE *fp;
fp = fopen("/proc/cgroups", "r");
if (!fp) {
@@ -53,7 +98,9 @@ void cgroup_init(void)
}
/* Skip first line, header */
fgets(buf, sizeof(buf), fp);
(void)fgets(buf, sizeof(buf), fp);
/* Create and mount traditional cgroups v1 hier */
while (fgets(buf, sizeof(buf), fp)) {
char *cgroup;
char rc[80];
@@ -63,46 +110,71 @@ void cgroup_init(void)
continue;
snprintf(rc, sizeof(rc), "/sys/fs/cgroup/%s", cgroup);
#if 0
if (mkdir(rc, 0755) && EEXIST != errno)
continue;
if (mount("cgroup", rc, "cgroup", opts, cgroup))
_d("Failed mounting %s cgroup on %s", cgroup, rc);
#else
symlink("finit", rc);
append_ctrl(cgroup);
#endif
}
/* Default cgroups for process monitoring */
if (mkdir("/sys/fs/cgroup/finit", 0755) && EEXIST != errno)
/* Finit default cgroups for process monitoring */
if (mkdir(FINIT_CGPATH, 0755) && EEXIST != errno)
goto fail;
if (mount("none", "/sys/fs/cgroup/finit", "cgroup", opts, "none,name=finit")) {
#if 0
strlcpy(controllers, "none,name=finit", sizeof(controllers));
#else
strlcat(controllers, ",name=finit", sizeof(controllers));
#endif
if (mount("none", FINIT_CGPATH, "cgroup", opts, controllers)) {
_pe("Failed mounting Finit cgroup hierarchy");
goto fail;
}
/* Set up reaper and enable callbacks */
echo("/sys/fs/cgroup/finit/release_agent", 0, FINIT_LIBPATH_ "/cgreaper.sh");
echo("/sys/fs/cgroup/finit/notify_on_release", 0, "1");
echo(FINIT_CGPATH "/release_agent", 0, FINIT_LIBPATH_ "/cgreaper.sh");
echo(FINIT_CGPATH "/notify_on_release", 0, "1");
/* Default groups, PID 1, services, and user/login processes */
if (mkdir("/sys/fs/cgroup/finit/init", 0755) && EEXIST != errno)
if (mkdir(FINIT_CGPATH "/init", 0755) && EEXIST != errno)
goto fail;
if (mkdir("/sys/fs/cgroup/finit/system", 0755) && EEXIST != errno)
if (mkdir(FINIT_CGPATH "/system", 0755) && EEXIST != errno)
goto fail;
if (mkdir("/sys/fs/cgroup/finit/user", 0755) && EEXIST != errno)
if (mkdir(FINIT_CGPATH "/user", 0755) && EEXIST != errno)
goto fail;
/*
* Set up basic limits for our groups. Finit optimized defaults
* for embedded and server systems include limiting the groups
* init and user to a single CPU with 10% share, the system
* group gets the rest. Override this from finit.conf
*/
#if 0
#else
snprintf(buf, sizeof(buf), "0-%d", get_nprocs_conf() - 1);
group_init(FINIT_CGPATH "/init", "0", 50);
group_init(FINIT_CGPATH "/user", "0", 75);
group_init(FINIT_CGPATH "/system", buf, 900);
#endif
/* Move ourselves to init */
echo("/sys/fs/cgroup/finit/init/cgroup.procs", 0, "1");
echo(FINIT_CGPATH "/init/cgroup.procs", 0, "1");
/* We have signal, main screen turn on! */
cg_init = 1;
fail:
fclose(fp);
}
static int move_pid(char *group, char *name, char *id, int pid)
static int move_pid(char *group, char *name, int pid)
{
char path[256];
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s/%s:%s", group, name, id);
snprintf(path, sizeof(path), "/sys/fs/cgroup/%s/%s", group, name);
if (mkdir(path, 0755) && errno != EEXIST)
return 1;
@@ -111,25 +183,19 @@ static int move_pid(char *group, char *name, char *id, int pid)
return echo(path, 0, "%d", pid);
}
int cgroup_user(char *name)
int cgroup_user(char *name, int pid)
{
if (!cg_init)
return 0;
return move_pid("finit/user", name, "0", getpid());
return move_pid("finit/user", name, pid);
}
int cgroup_service(char *nm, char *id, int pid)
int cgroup_service(char *name, int pid)
{
if (!cg_init)
return 0;
if (pid <= 0) {
errno = EINVAL;
return 1;
}
return move_pid("finit/system", nm, id, pid);
return move_pid("finit/system", name, pid);
}
/**
+2 -2
View File
@@ -26,7 +26,7 @@
void cgroup_init (void);
int cgroup_user (char *name);
int cgroup_service (char *cmd, char *id, int pid);
int cgroup_user (char *name, int pid);
int cgroup_service (char *name, int pid);
#endif /* FINIT_CGROUP_H_ */
+483
View File
@@ -0,0 +1,483 @@
/* New client tool, replaces old /dev/initctl API and telinit tool
*
* Copyright (c) 2019-2021 Joachim Wiberg <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "config.h"
#include <err.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <search.h>
#include <inttypes.h>
#include <lite/lite.h>
#include <sys/sysinfo.h> /* sysinfo() */
#include "initctl.h"
#define CDIM plain ? "" : "\e[2m"
#define CRST plain ? "" : "\e[0m"
#define NONE " "
#define PIPE plain ? "| " : "│ "
#define FORK plain ? "|-" : "├─"
#define END plain ? "`-" : "└─"
uint64_t total_ram; /* From sysinfo() */
struct cg {
struct cg *cg_next;
char *cg_path; /* path in /sys/fs/cgroup */
uint64_t cg_prev; /* cpuacct.usage */
uint64_t cg_rss; /* memory.stat */
uint64_t cg_vmlib; /* memory.stat */
uint64_t cg_vmsize; /* memory.usage_in_bytes */
float cg_mem; /* cg_rss / total_ram * 100 */
float cg_load; /* curr - prev / 10000000.0 */
};
struct cg dummy; /* empty result "NULL" */
struct cg *list;
static char *pid_cmdline(int pid, char *buf, size_t len)
{
size_t i, sz;
char *ptr;
FILE *fp;
fp = fopenf("r", "/proc/%d/cmdline", pid);
if (!fp) {
buf[0] = 0;
return buf;
}
sz = fread(buf, sizeof(buf[0]), len - 1, fp);
fclose(fp);
if (!sz)
return NULL; /* kernel thread */
buf[sz] = 0;
ptr = strchr(buf, 0);
if (ptr && ptr != buf) {
ptr++;
sz -= ptr - buf;
memmove(buf, ptr, sz + 1);
}
for (i = 0; i < sz; i++) {
if (buf[i] == 0)
buf[i] = ' ';
}
return buf;
}
static char *pid_comm(int pid, char *buf, size_t len)
{
FILE *fp;
fp = fopenf("r", "/proc/%d/comm", pid);
if (!fp)
return NULL;
fgets(buf, len, fp);
fclose(fp);
return chomp(buf);
}
static uint64_t cgroup_uint64(char *path, char *file)
{
uint64_t val = 0;
char buf[42];
FILE *fp;
fp = fopenf("r", "%s/%s", path, file);
if (!fp)
return val;
if (fgets(buf, sizeof(buf), fp))
val = strtoull(buf, NULL, 10);
fclose(fp);
return val;
}
static int cgroup_shares(char *path)
{
return (int)cgroup_uint64(path, "cpu.shares");
}
static uint64_t cgroup_memuse(struct cg *cg)
{
char buf[42];
FILE *fp;
fp = fopenf("r", "%s/memory.stat", cg->cg_path);
if (fp) {
while (fgets(buf, sizeof(buf), fp)) {
if (!strncmp(buf, "total_rss", 9)) {
cg->cg_rss = strtoull(&buf[10], NULL, 10);
continue;
}
if (!strncmp(buf, "total_mapped_file", 17)) {
cg->cg_vmlib = strtoull(&buf[18], NULL, 10);
break; /* done */
}
}
fclose(fp);
}
cg->cg_mem = (float)(cg->cg_rss * 100 / total_ram);
/* "fuzz value for efficient access", sort of like total_cache + total_rss */
return cg->cg_vmsize = cgroup_uint64(cg->cg_path, "memory.usage_in_bytes");
}
static char *memsz(uint64_t sz, char *buf, size_t len)
{
int gb, mb, kb, b;
gb = sz / (1024 * 1024 * 1024);
sz = sz % (1024 * 1024 * 1024);
mb = sz / (1024 * 1024);
sz = sz % (1024 * 1024);
kb = sz / (1024);
b = sz % (1024);
if (gb)
snprintf(buf, len, "%d.%dG", gb, mb / 102);
else if (mb)
snprintf(buf, len, "%d.%dM", mb, kb / 102);
else
snprintf(buf, len, "%d.%dk", kb, b / 102);
return buf;
}
static float cgroup_cpuload(struct cg *cg)
{
char fn[256];
char buf[64];
FILE *fp;
snprintf(fn, sizeof(fn), "%s/cpuacct.usage", cg->cg_path);
fp = fopen(fn, "r");
if (!fp)
err(1, "Cannot open %s", fn);
if (fgets(buf, sizeof(buf), fp)) {
uint64_t curr;
curr = strtoull(buf, NULL, 10);
if (cg->cg_prev != 0) {
uint64_t diff = curr - cg->cg_prev;
/* this expects 1 sec poll interval */
cg->cg_load = (float)(diff / 1000000);
cg->cg_load /= 10.0;
}
cg->cg_prev = curr;
}
fclose(fp);
return cg->cg_load;
}
static struct cg *append(char *path)
{
struct cg *cg;
char fn[256];
ENTRY item;
snprintf(fn, sizeof(fn), "%s/cpuacct.usage", path);
if (access(fn, F_OK)) {
warn("not a cgroup path with cpuacct controller, %s", path);
return NULL;
}
cg = calloc(1, sizeof(struct cg));
if (!cg)
err(1, "failed allocating struct cg");
cg->cg_path = strdup(path);
if (list)
cg->cg_next = list;
list = cg;
item.key = cg->cg_path;
item.data = cg;
if (!hsearch(item, ENTER))
err(1, "failed adding to hash table");
return cg;
}
static struct cg *find(char *path)
{
ENTRY *ep, item = { path, NULL };
ep = hsearch(item, FIND);
if (ep)
return ep->data;
return append(path);
}
static struct cg *cg_update(char *path)
{
struct cg *cg;
cg = find(path);
if (!cg)
return &dummy;
cgroup_cpuload(cg);
cgroup_memuse(cg);
return cg;
}
static int cgroup_filter(const struct dirent *entry)
{
/* Skip current dir ".", and prev dir "..", from list of files */
if ((1 == strlen(entry->d_name) && entry->d_name[0] == '.') ||
(2 == strlen(entry->d_name) && !strcmp(entry->d_name, "..")))
return 0;
if (entry->d_name[0] == '.')
return 0;
if (entry->d_type != DT_DIR)
return 0;
return 1;
}
static int cgroup_tree(char *path, char *pfx, int top)
{
struct dirent **namelist = NULL;
char s[32], r[32], l[32];
char row[screen_cols + 9]; /* + control codes */
size_t rlen = sizeof(row) - 1;
size_t rplen = rlen - 9;
struct stat st;
struct cg *cg;
char buf[512];
int rc = 0;
FILE *fp;
int i, n;
int num;
if (top >= screen_rows)
return 0;
if (-1 == lstat(path, &st))
return 1;
if ((st.st_mode & S_IFMT) != S_IFDIR) {
errno = ENOTDIR;
return -1;
}
fp = fopenf("r", "%s/cgroup.procs", path);
if (!fp)
return -1;
num = 0;
while (fgets(buf, sizeof(buf), fp))
num++;
if (!pfx) {
pfx = "";
if (top) {
cg = cg_update(path);
snprintf(row, rplen, " %6.6s %6.6s %6.6s %5.1f %5.1f [%s]",
memsz(cg->cg_vmsize, s, sizeof(s)),
memsz(cg->cg_rss, r, sizeof(r)),
memsz(cg->cg_vmlib, l, sizeof(l)),
cg->cg_mem, cg->cg_load, path);
} else
strlcpy(row, path, rplen);
puts(row);
}
n = scandir(path, &namelist, cgroup_filter, alphasort);
if (n > 0) {
for (i = 0; i < n; i++) {
char *nm = namelist[i]->d_name;
char prefix[80], tmp[42];
snprintf(buf, sizeof(buf), "%s/%s", path, nm);
if (top) {
cg = cg_update(buf);
snprintf(row, rplen,
" %6.6s %6.6s %6.6s %5.1f %5.1f ",
memsz(cg->cg_vmsize, s, sizeof(s)),
memsz(cg->cg_rss, r, sizeof(r)),
memsz(cg->cg_vmlib, l, sizeof(l)),
cg->cg_mem, cg->cg_load);
} else
row[0] = 0;
strlcat(row, pfx, rplen);
if (i + 1 == n) {
strlcat(row, END, rplen);
snprintf(prefix, sizeof(prefix), "%s ", pfx);
} else {
strlcat(row, FORK, rplen);
snprintf(prefix, sizeof(prefix), "%s%s ", pfx, PIPE);
}
strlcat(row, " ", rplen);
strlcat(row, nm, rplen);
strlcat(row, "/ ", rplen);
snprintf(tmp, sizeof(tmp), "[cpu.shares: %d]", cgroup_shares(buf));
strlcat(row, tmp, rplen);
puts(row);
rc += cgroup_tree(buf, prefix, top ? top + i : 0);
free(namelist[i]);
}
free(namelist);
}
if (num > 0) {
rewind(fp);
i = 0;
while (fgets(buf, sizeof(buf), fp)) {
char comm[80] = { 0 };
pid_t pid;
pid = atoi(chomp(buf));
if (pid <= 0)
continue;
/* skip kernel threads for now (no cmdline) */
pid_comm(pid, comm, sizeof(comm));
if (pid_cmdline(pid, buf, sizeof(buf))) {
char proc[screen_cols];
int len;
if (top)
snprintf(row, rplen, "%37s", " ");
else
row[0] = 0;
strlcat(row, pfx, rplen);
strlcat(row, ++i == num ? END : FORK, rlen);
snprintf(proc, sizeof(proc), " %d %s %s", pid, comm, buf);
if (plain) {
strlcat(row, proc, rplen);
} else {
strlcat(row, CDIM, rlen);
strlcat(row, proc, rlen);
len = strlen(row) + strlen(CRST);
if (len > (int)rlen)
row[rlen - strlen(CRST)] = 0;
strlcat(row, CRST, sizeof(row));
}
puts(row);
}
if (top) {
top += i;
if (top >= screen_rows)
break;
}
}
return fclose(fp);
}
fclose(fp);
return rc;
}
int show_cgroup(char *arg)
{
char path[512];
if (!arg)
arg = FINIT_CGPATH;
else if (arg[0] != '/') {
paste(path, sizeof(path), FINIT_CGPATH, arg);
arg = path;
}
return cgroup_tree(arg, NULL, 0);
}
int show_cgtop(char *arg)
{
struct sysinfo si = { 0 };
char path[512];
int laps = 0;
if (!arg)
arg = FINIT_CGPATH;
else if (arg[0] != '/') {
paste(path, sizeof(path), FINIT_CGPATH, arg);
arg = path;
}
if (!hcreate(screen_rows + 25))
err(1, "failed creating hash table");
sysinfo(&si);
total_ram = si.totalram * si.mem_unit;
while (1) {
fputs("\e[2J\e[1;1H", stdout);
if (heading)
print_header(" VmSIZE RSS VmLIB %%MEM %%CPU GROUP");
cgroup_tree(arg, NULL, 1);
if (ionce && laps++)
break;
sleep(1);
}
return 0;
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+37
View File
@@ -0,0 +1,37 @@
/* Cgroup utility functions for initctl
*
* Copyright (c) 2019-2021 Joachim Wiberg <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef FINIT_CGUTIL_H_
#define FINIT_CGUTIL_H_
int show_cgroup (char *arg);
int show_cgtop (char *arg);
#endif /* FINIT_CGUTIL_H_ */
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+4 -8
View File
@@ -21,6 +21,7 @@
* THE SOFTWARE.
*/
#include <err.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/un.h>
@@ -38,17 +39,12 @@ int client_connect(void)
sd = socket(AF_UNIX, SOCK_STREAM, 0);
if (-1 == sd)
goto error;
err(1, "Failed creating UNIX domain socket");
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1) {
close(sd);
goto error;
}
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1)
err(1, "Failed connecting to finit");
return sd;
error:
perror("Failed connecting to finit");
return -1;
}
int client_disconnect(void)
+13 -31
View File
@@ -99,13 +99,13 @@ static void cond_bump_reconf(void)
unsigned int rgen;
/*
* If %COND_RECONF does not exist, cond_get_gen() returns 0
* If %_PATH_RECONF does not exist, cond_get_gen() returns 0
* meaning that rgen++ is always what we want.
*/
rgen = cond_get_gen(COND_RECONF);
rgen = cond_get_gen(_PATH_RECONF);
rgen++;
cond_set_gen(COND_RECONF, rgen);
cond_set_gen(_PATH_RECONF, rgen);
}
static int cond_checkpath(const char *path)
@@ -119,7 +119,7 @@ static int cond_checkpath(const char *path)
return 1;
}
if (makepath(dir) && errno != EEXIST) {
if (mkpath(dir, 0755) && errno != EEXIST) {
_pe("Failed creating dir '%s' for condition '%s'", dir, path);
return 1;
}
@@ -134,7 +134,7 @@ int cond_set_path(const char *path, enum cond_state new)
_d("%s", path);
rgen = cond_get_gen(COND_RECONF);
rgen = cond_get_gen(_PATH_RECONF);
if (!rgen) {
_e("Unable to read configuration generation (%s)", path);
return -1;
@@ -162,27 +162,8 @@ int cond_set_path(const char *path, enum cond_state new)
return new != old;
}
/* Has condition in configuration and cond is allowed? */
static int svc_has_cond(svc_t *svc)
{
if (!svc->cond[0])
return 0;
switch (svc->type) {
case SVC_TYPE_SERVICE:
case SVC_TYPE_TASK:
case SVC_TYPE_RUN:
case SVC_TYPE_SYSV:
return 1;
default:
break;
}
return 0;
}
static void cond_update(const char *name)
/* Should only be used by cond_set*(), cond_clear(), and usr plugin! */
void cond_update(const char *name)
{
svc_t *svc, *iter = NULL;
@@ -221,7 +202,8 @@ void cond_set_oneshot(const char *name)
if (cond_checkpath(path))
return;
symlink(COND_RECONF, path);
if (symlink(_PATH_RECONF, path) && errno != EEXIST)
_pe("Failed creating onshot cond %s", name);
cond_update(name);
}
@@ -254,13 +236,13 @@ static int reassert(const char *fpath, const struct stat *sb, int tflg, struct F
if (tflg != FTW_F)
return 0;
nm = strstr((char *)fpath, COND_DIR);
nm = strstr((char *)fpath, COND_BASE);
if (!nm) {
_e("Incorrect condtion path %s, cannot reassert", fpath);
return 1;
}
nm += sizeof(COND_DIR);
nm += strlen(COND_BASE);
_d("Reasserting %s => %s", fpath, nm);
cond_set(nm);
@@ -281,8 +263,8 @@ void cond_init(void)
{
char path[MAX_ARG_LEN];
if (makepath(pid_runpath(COND_PATH, path, sizeof(path))) && errno != EEXIST) {
_pe("Failed creating condition base directory '%s'", COND_PATH);
if (mkpath(pid_runpath(_PATH_COND, path, sizeof(path)), 0755) && errno != EEXIST) {
_pe("Failed creating condition base directory '%s'", _PATH_COND);
return;
}
+5 -5
View File
@@ -45,7 +45,7 @@ const char *cond_path(const char *name)
static char path[256];
char tmp[MAX_ARG_LEN];
snprintf(tmp, sizeof(tmp), COND_PATH "/%s", name);
snprintf(tmp, sizeof(tmp), _PATH_COND "%s", name);
return pid_runpath(tmp, path, sizeof(path));
}
@@ -74,14 +74,14 @@ enum cond_state cond_get_path(const char *path)
{
int cgen, rgen;
rgen = cond_get_gen(COND_RECONF);
if (!rgen)
return COND_OFF;
cgen = cond_get_gen(path);
if (!cgen)
return COND_OFF;
rgen = cond_get_gen(_PATH_RECONF);
if (!rgen)
return COND_OFF;
return (cgen == rgen) ? COND_ON : COND_FLUX;
}
+7 -3
View File
@@ -4,9 +4,12 @@
#include <paths.h>
#include <svc.h>
#define COND_DIR "finit/cond"
#define COND_PATH _PATH_VARRUN COND_DIR
#define COND_RECONF COND_PATH "/reconf"
#define COND_BASE "finit/cond"
#define COND_USR "usr/"
#define _PATH_COND _PATH_VARRUN COND_BASE "/"
#define _PATH_CONDUSR _PATH_COND COND_USR
#define _PATH_RECONF _PATH_COND "reconf"
typedef enum cond_state {
COND_OFF = 0,
@@ -23,6 +26,7 @@ enum cond_state cond_get (const char *name);
enum cond_state cond_get_agg (const char *names);
int cond_affects (const char *name, const char *names);
void cond_update (const char *name);
int cond_set_path (const char *path, enum cond_state new);
void cond_set (const char *name);
void cond_set_oneshot (const char *name);
+38 -21
View File
@@ -47,6 +47,7 @@
int logfile_size_max = 200000; /* 200 kB */
int logfile_count_max = 5;
struct rlimit initial_rlimit[RLIMIT_NLIMITS];
struct rlimit global_rlimit[RLIMIT_NLIMITS];
struct conf_change {
@@ -78,14 +79,14 @@ static void hide_args(int argc, char *argv[])
}
}
int get_bool(char *ptr, int default_value)
int get_bool(char *arg, int default_value)
{
if (!ptr)
if (!arg)
goto fallback;
if (string_compare(ptr, "true") || string_compare(ptr, "on") || string_compare(ptr, "1"))
if (string_compare(arg, "true") || string_compare(arg, "on") || string_compare(arg, "1"))
return 1;
if (string_compare(ptr, "false") || string_compare(ptr, "off") || string_compare(ptr, "0"))
if (string_compare(arg, "false") || string_compare(arg, "off") || string_compare(arg, "0"))
return 0;
fallback:
return default_value;
@@ -97,19 +98,24 @@ fallback:
*/
static void parse_finit_opts(char *opt)
{
char *ptr;
char *arg;
ptr = strchr(opt, '=');
if (ptr)
*ptr++ = 0;
arg = strchr(opt, '=');
if (arg)
*arg++ = 0;
if (string_compare(opt, "debug")) {
debug = get_bool(ptr, 1);
debug = get_bool(arg, 1);
return;
}
if (string_compare(opt, "status_style")) {
if (string_compare(ptr, "old") || string_compare(ptr, "classic"))
if (!arg) {
_e("status_style option requires an argument, skipping.");
return;
}
if (string_compare(arg, "old") || string_compare(arg, "classic"))
show_progress(PROGRESS_CLASSIC);
else
show_progress(PROGRESS_MODERN);
@@ -117,7 +123,7 @@ static void parse_finit_opts(char *opt)
}
if (string_compare(opt, "show_status")) {
show_progress(get_bool(ptr, 1) ? PROGRESS_DEFAULT : PROGRESS_SILENT);
show_progress(get_bool(arg, 1) ? PROGRESS_DEFAULT : PROGRESS_SILENT);
return;
}
}
@@ -620,12 +626,9 @@ int conf_reload(void)
tty_mark();
/*
* Get current global limits, which may be overridden from both
* finit.conf, for Finit and its services like getty+watchdogd,
* and *.conf in finit.d/, for each service(s) listed there.
* Reset global rlimit to bootstrap values from conf_init().
*/
for (int i = 0; i < RLIMIT_NLIMITS; i++)
getrlimit(i, &global_rlimit[i]);
memcpy(global_rlimit, initial_rlimit, sizeof(global_rlimit));
if (rescue) {
int rc;
@@ -694,6 +697,8 @@ int conf_reload(void)
globfree(&gl);
/* Mark any reverse deps as chenaged. */
service_update_rdeps();
done:
/* Drop record of all .conf changes */
drop_changes();
@@ -716,7 +721,6 @@ static struct conf_change *conf_find(char *file)
struct conf_change *node, *tmp;
TAILQ_FOREACH_SAFE(node, &conf_change_list, link, tmp) {
_d("file: %s vs changed file: %s", file, node->name);
if (string_compare(node->name, file))
return node;
}
@@ -801,7 +805,8 @@ static void conf_cb(uev_t *w, void *arg, int events)
{
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
struct inotify_event *ev;
ssize_t sz, off;
ssize_t sz;
size_t off;
sz = read(w->fd, ev_buf, sizeof(ev_buf) - 1);
if (sz <= 0) {
@@ -810,10 +815,16 @@ static void conf_cb(uev_t *w, void *arg, int events)
}
ev_buf[sz] = 0;
for (off = 0; off < sz; off += sizeof(*ev) + ev->len) {
for (off = 0; off < (size_t)sz; off += sizeof(*ev) + ev->len) {
struct iwatch_path *iwp;
if (off + sizeof(*ev) > (size_t)sz)
break;
ev = (struct inotify_event *)&ev_buf[off];
if (off + sizeof(*ev) + ev->len > (size_t)sz)
break;
if (!ev->mask)
continue;
@@ -883,8 +894,14 @@ int conf_init(uev_ctx_t *ctx)
* finit.conf, for Finit and its services like getty+watchdogd,
* and *.conf in finit.d/, for each service(s) listed there.
*/
for (int i = 0; i < RLIMIT_NLIMITS; i++)
getrlimit(i, &global_rlimit[i]);
for (int i = 0; i < RLIMIT_NLIMITS; i++) {
if (getrlimit(i, &initial_rlimit[i]))
logit(LOG_WARNING, "rlimit: Failed reading setting %s: %s",
rlim2str(i), strerror(errno));
}
/* Initialize global rlimits, e.g. for built-in services */
memcpy(global_rlimit, initial_rlimit, sizeof(global_rlimit));
/* prepare /etc watcher */
fd = iwatch_init(&iw_conf);
+23 -15
View File
@@ -179,7 +179,8 @@ static void fs_remount_root(int fsckerr)
* XXX: Untested, in the initramfs age we should
* probably use switch_root instead.
*/
rc = mount(SYSROOT, "/", NULL, MS_MOVE, NULL);
if (mount(SYSROOT, "/", NULL, MS_MOVE, NULL))
_pe("Failed %s / MS_MOVE");
}
#endif /* SYSROOT */
@@ -198,7 +199,6 @@ static void fs_mount(void)
plugin_run_hooks(HOOK_MOUNT_POST);
run("swapon -ea");
umask(0022);
}
/*
@@ -206,6 +206,9 @@ static void fs_mount(void)
* for early multi-console, and /sys for the cgroups support. Any
* occurrence of these file systems in /etc/fstab will replace these
* mounts later in fs_mount()
*
* Ignore any mount errors with EBUSY, kernel likely alread mounted
* the filesystem for us automatically, e.g., CONFIG_DEVTMPFS_MOUNT.
*/
static void fs_init(void)
{
@@ -218,8 +221,16 @@ static void fs_init(void)
};
size_t i;
for (i = 0; i < NELEMS(fs); i++)
mount(fs[i].spec, fs[i].file, fs[i].type, 0, NULL);
/* mask writable bit for g and o */
umask(022);
for (i = 0; i < NELEMS(fs); i++) {
int rc;
rc = mount(fs[i].spec, fs[i].file, fs[i].type, 0, NULL);
if (rc && errno != EBUSY)
_pe("Failed mounting %s on %s", fs[i].spec, fs[i].file);
}
}
/*
@@ -439,7 +450,7 @@ int main(int argc, char *argv[])
};
struct wq final = {
.cb = final_worker,
.delay = 1000
.delay = 100
};
uev_ctx_t loop;
@@ -470,17 +481,13 @@ int main(int argc, char *argv[])
ctx = &loop;
/*
* Set PATH, SHELL, PWD, and umask early to something sane
* Set PATH, SHELL, and PWD early to something sane
*/
setenv("PATH", _PATH_STDPATH, 1);
setenv("SHELL", _PATH_BSHELL, 1);
chdir("/");
umask(0);
/* Set up canvas */
if (!rescue && !debug)
screen_init();
if (chdir("/"))
_pe("Failed cd /");
/* Allow progress, if enabled */
enable_progress(1);
@@ -490,8 +497,10 @@ int main(int argc, char *argv[])
*/
emergency_shell();
/* Load plugins early, the first hook is in banner(), so we
* need plugins loaded before calling it. */
/*
* Load plugins early, the first hook is in banner(), so we
* need plugins loaded before calling it.
*/
plugin_init(&loop);
/*
@@ -540,7 +549,6 @@ int main(int argc, char *argv[])
_d("Starting initctl API responder ...");
api_init(&loop);
umask(022);
_d("Starting the big state machine ...");
schedule_work(&crank);
+6 -2
View File
@@ -38,11 +38,15 @@
/* just in case */
#ifndef _PATH_STDPATH
#define _PATH_STDPATH "/usr/bin:/bin:/usr/sbin:/sbin"
#define _PATH_STDPATH "/usr/bin:/bin:/usr/sbin:/sbin"
#endif
#ifndef _PATH_VARRUN
#define _PATH_VARRUN "/var/run/"
#define _PATH_VARRUN "/var/run/"
#endif
#ifndef FINIT_CGPATH
#define FINIT_CGPATH "/sys/fs/cgroup/finit"
#endif
#define LOGIT_PATH FINIT_LIBPATH_ "/logit"
+1 -1
View File
@@ -186,7 +186,7 @@ static int do_login(char *name)
{
struct stat st;
cgroup_user(name);
cgroup_user(name, getpid());
execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL);
/*
+76 -8
View File
@@ -103,6 +103,9 @@ static void add_console(char *cons)
if (ptr)
*ptr = 0;
if (!strncmp(cons, "/dev/", 5))
cons += 5;
snprintf(path, sizeof(path), "/dev/%s", cons);
/* check for and skip duplicates */
@@ -121,7 +124,7 @@ static void add_console(char *cons)
num_cons++;
}
void console_init(void)
static void find_active_consoles(void)
{
char *cons, *ptr, *tok;
char buf[512];
@@ -131,7 +134,7 @@ void console_init(void)
if (!fp) {
fallback:
_d("Using fallback /dev/console");
add_console("/dev/console");
add_console("console");
return;
}
@@ -148,6 +151,12 @@ void console_init(void)
}
}
void console_init(void)
{
find_active_consoles();
screen_init();
}
ssize_t cprintf(const char *fmt, ...)
{
const size_t len = strlen(fmt) * 2;
@@ -404,6 +413,63 @@ int getgroup(char *group)
#endif
}
/* get current user */
int getcuser(char *buf, size_t len)
{
char *ptr = "root";
#ifndef ENABLE_STATIC
struct passwd *pw = getpwuid(getuid());
if (pw)
ptr = pw->pw_name;
#endif
if (strlcpy(buf, ptr, len) > len)
return -1;
return 0;
}
/* get current group */
int getcgroup(char *buf, size_t len)
{
char *ptr = "root";
#ifndef ENABLE_STATIC
struct group *gr = getgrgid(getgid());
if (gr)
ptr = gr->gr_name;
#endif
if (strlcpy(buf, ptr, len) > len)
return -1;
return 0;
}
int mksubsys(const char *dir, mode_t mode, char *user, char *group)
{
mode_t omask;
int uid, gid;
int rc = 0;
omask = umask(0);
uid = getuser(user, NULL);
if (uid >= 0) {
gid = getgroup(group);
if (gid < 0)
gid = 0;
rc = makedir(dir, mode);
chown(dir, uid, gid);
}
umask(omask);
return rc;
}
void set_hostname(char **hostname)
{
FILE *fp;
@@ -426,14 +492,14 @@ void set_hostname(char **hostname)
return;
}
fgets(*hostname, st.st_size, fp);
chomp(*hostname);
if (fgets(*hostname, st.st_size, fp))
chomp(*hostname);
fclose(fp);
}
done:
if (*hostname)
sethostname(*hostname, strlen(*hostname));
(void)sethostname(*hostname, strlen(*hostname));
}
/*
@@ -557,10 +623,12 @@ int ismnt(char *file, char *dir, char *mode)
while ((mnt = getmntent(fp))) {
if (!strcmp(mnt->mnt_dir, dir)) {
if (mode && hasopt(mnt->mnt_opts, mode))
found = 1;
else
if (mode) {
if (hasopt(mnt->mnt_opts, mode))
found = 1;
} else
found = 1;
break;
}
}
+6
View File
@@ -81,6 +81,12 @@ int print_result (int fail);
int getuser (char *username, char **home);
int getgroup (char *group);
int getcuser (char *buf, size_t len);
int getcgroup (char *buf, size_t len);
int mksubsys (const char *dir, mode_t mode, char *user, char *group);
void set_hostname (char **hostname);
void networking (int updown);
+330 -237
View File
@@ -39,20 +39,21 @@
#include "cond.h"
#include "serv.h"
#include "service.h"
#include "cgutil.h"
#include "util.h"
#define _PATH_COND _PATH_VARRUN "finit/cond/"
struct command {
char *cmd;
int (*cb)(char *arg);
struct cmd {
char *cmd;
struct cmd *ctx;
int (*cb)(char *arg);
};
int icreate = 0;
int iforce = 0;
int ionce = 0;
int heading = 1;
int numeric = 0;
int verbose = 0;
int plain = 0;
int runlevel = 0;
int iw, pw;
@@ -72,11 +73,7 @@ static void col_widths(void)
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
int w, p;
if (!numeric)
svc_ident(svc, ident, sizeof(ident));
else
svc_jobid(svc, ident, sizeof(ident));
svc_ident(svc, ident, sizeof(ident));
w = (int)strlen(ident);
if (w > iw)
iw = w;
@@ -93,6 +90,30 @@ static void col_widths(void)
pw = 3;
}
void print_header(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (plain) {
int len;
vfprintf(stdout, fmt, ap);
fputs("\n", stdout);
for (len = 0; len < screen_cols; len++)
fputc('=', stdout);
fputs("\n", stdout);
} else {
char buf[screen_cols];
vsnprintf(buf, sizeof(buf), fmt, ap);
printheader(stdout, buf, 0);
}
va_end(ap);
}
static int runlevel_get(int *prevlevel)
{
int result;
@@ -124,7 +145,6 @@ static int toggle_debug(char *arg)
static int do_log(char *svc)
{
char cmd[128];
char *logfile = "/var/log/syslog";
if (!svc || !svc[0])
@@ -133,9 +153,7 @@ static int do_log(char *svc)
if (!fexist(logfile))
logfile = "/var/log/messages";
snprintf(cmd, sizeof(cmd), "cat %s | grep %s | tail -10", logfile, svc);
return system(cmd);
return systemf("cat %s | grep %s | tail -10", logfile, svc);
}
static int do_runlevel(char *arg)
@@ -143,10 +161,9 @@ static int do_runlevel(char *arg)
struct init_request rq = {
.magic = INIT_MAGIC,
.cmd = INIT_CMD_RUNLVL,
.runlevel = (int)arg[0],
};
if (!rq.runlevel) {
if (!arg) {
int prevlevel = 0;
int runlevel;
char prev;
@@ -165,6 +182,9 @@ static int do_runlevel(char *arg)
return 0;
}
/* set runlevel */
rq.runlevel = (int)arg[0];
return client_send(&rq, sizeof(rq));
}
@@ -195,10 +215,17 @@ static int do_startstop(int cmd, char *arg)
.cmd = INIT_CMD_SVC_QUERY
};
if (!arg || !arg[0])
errx(1, "missing argument to %s", (cmd == INIT_CMD_START_SVC
? "start"
: (cmd == INIT_CMD_STOP_SVC
? "stop"
: "restart")));
strlcpy(rq.data, arg, sizeof(rq.data));
if (client_send(&rq, sizeof(rq))) {
fprintf(stderr, "No such job(s) or service(s): %s\n\n", rq.data);
fprintf(stderr, "Usage: initctl %s <JOB|NAME>[:ID]\n",
fprintf(stderr, "No such task or service(s): %s\n\n", arg);
fprintf(stderr, "Usage: initctl %s <NAME>[:ID]\n",
cmd == INIT_CMD_START_SVC ? "start" :
(cmd == INIT_CMD_STOP_SVC ? "stop" : "restart"));
return 1;
@@ -220,7 +247,7 @@ static int dump_one_cond(const char *fpath, const struct stat *sb, int tflag, st
if (tflag != FTW_F)
return 0;
if (!strcmp(fpath, _PATH_COND "reconf"))
if (!strcmp(fpath, _PATH_RECONF))
return 0;
asserted = condstr(cond_get_path(fpath));
@@ -236,9 +263,15 @@ static int dump_one_cond(const char *fpath, const struct stat *sb, int tflag, st
nm = svc_ident(svc, NULL, 0);
pid = svc->pid;
}
} else if (strncmp("usr/", cond, 4) == 0) {
nm = "static";
pid = 0;
} else if (strncmp("hook/", cond, 4) == 0) {
nm = "static";
pid = 1;
}
printf("%-*d %-*s %-6s %s\n", pw, pid, iw, nm, asserted, cond);
printf("%-*d %-*s %-6s <%s>\n", pw, pid, iw, nm, asserted, cond);
return 0;
}
@@ -251,7 +284,7 @@ static int do_cond_dump(char *arg)
snprintf(title, sizeof(title), "%-*s %-*s %-6s %s", pw, "PID",
iw, "IDENT", "STATUS", "CONDITION");
printheader(NULL, title, 0);
print_header(title);
}
if (nftw(_PATH_COND, dump_one_cond, 20, 0) == -1) {
@@ -262,41 +295,93 @@ static int do_cond_dump(char *arg)
return 0;
}
static void show_cond_one(const char *_conds)
static int do_cond_act(char *arg, int creat)
{
static char conds[MAX_COND_LEN];
char *cond;
char oneshot[256];
size_t off;
strlcpy(conds, _conds, sizeof(conds));
if (arg && strncmp(arg, COND_USR, strlen(COND_USR)) == 0)
arg += strlen(COND_USR);
putchar('<');
if (!arg || !arg[0])
errx(1, "Invalid condition (empty)");
if (strchr(arg, '/'))
errx(1, "Invalid condition (slashes)");
if (strchr(arg, '.'))
errx(1, "Invalid condition (periods)");
snprintf(oneshot, sizeof(oneshot), _PATH_CONDUSR "%s", arg);
off = strlen(_PATH_COND);
if (creat) {
if (symlink(_PATH_RECONF, oneshot) && errno != EEXIST)
err(1, "Failed asserting condition <%s>", &oneshot[off]);
} else {
if (erase(oneshot))
err(1, "Failed deasserting condition <%s>", &oneshot[off]);
}
return 0;
}
static int do_cond_set(char *arg) { return do_cond_act(arg, 1); }
static int do_cond_clr(char *arg) { return do_cond_act(arg, 0); }
static char *svc_cond(svc_t *svc, char *buf, size_t len)
{
char *cond, *conds;
memset(buf, 0, len);
if (!svc->cond[0])
return buf;
conds = strdup(svc->cond);
if (!conds)
return buf;
strlcat(buf, "<", len);
for (cond = strtok(conds, ","); cond; cond = strtok(NULL, ",")) {
if (cond != conds)
putchar(',');
strlcat(buf, ",", len);
switch (cond_get(cond)) {
case COND_ON:
printf("+%s", cond);
strlcat(buf, "+", len);
strlcat(buf, cond, len);
break;
case COND_FLUX:
printf("\e[1m~%s\e[0m", cond);
if (!plain)
strlcat(buf, "\e[1m", len);
strlcat(buf, "~", len);
strlcat(buf, cond, len);
if (!plain)
strlcat(buf, "\e[0m", len);
break;
case COND_OFF:
printf("\e[1m-%s\e[0m", cond);
if (!plain)
strlcat(buf, "\e[1m", len);
strlcat(buf, "-", len);
strlcat(buf, cond, len);
if (!plain)
strlcat(buf, "\e[0m", len);
break;
}
}
putchar('>');
strlcat(buf, ">", len);
free(conds);
return buf;
}
static int do_cond_show(char *arg)
{
char ident[MAX_IDENT_LEN];
enum cond_state cond;
char buf[512];
svc_t *svc;
col_widths();
@@ -305,7 +390,7 @@ static int do_cond_show(char *arg)
snprintf(title, sizeof(title), "%-*s %-*s %-6s %s", pw, "PID",
iw, "IDENT", "STATUS", "CONDITION (+ ON, ~ FLUX, - OFF)");
printheader(NULL, title, 0);
print_header(title);
}
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
@@ -314,47 +399,20 @@ static int do_cond_show(char *arg)
cond = cond_get_agg(svc->cond);
if (!numeric)
svc_ident(svc, ident, sizeof(ident));
else
svc_jobid(svc, ident, sizeof(ident));
printf("%-*d %-*s ", pw, svc->pid, iw, ident);
svc_ident(svc, buf, sizeof(buf));
printf("%-*d %-*s ", pw, svc->pid, iw, buf);
if (cond == COND_ON)
printf("%-6.6s ", condstr(cond));
else
printf("\e[1m%-6.6s\e[0m ", condstr(cond));
show_cond_one(svc->cond);
puts("");
puts(svc_cond(svc, buf, sizeof(buf)));
}
return 0;
}
static int do_cond(char *cmd)
{
int c;
char *arg;
struct command command[] = {
{ "show", do_cond_show },
{ "dump", do_cond_dump },
{ NULL, NULL }
};
arg = strpbrk(cmd, " \0");
if (arg)
*arg++ = 0;
for (c = 0; command[c].cmd; c++) {
if (string_match(command[c].cmd, cmd))
return command[c].cb(arg);
}
return do_cond_show(NULL);
}
static int do_cmd(int cmd)
{
struct init_request rq = {
@@ -382,15 +440,18 @@ int do_suspend (char *arg) { return do_cmd(INIT_CMD_SUSPEND); }
int utmp_show(char *file)
{
time_t sec;
struct utmp *ut;
static int once = 0;
struct tm *sectm;
struct utmp *ut;
time_t sec;
int pid;
char id[sizeof(ut->ut_id) + 1], user[sizeof(ut->ut_user) + 1], when[80];
if (heading)
printheader(NULL, file, 0);
if (heading) {
print_header("%s%s ", once ? "\n" : "", file);
once++;
}
utmpname(file);
setutent();
@@ -457,7 +518,7 @@ char *runlevel_string(int runlevel, int levels)
for (i = 0; i < 10; i++) {
if (ISSET(levels, i)) {
if (runlevel == i)
if (!plain && runlevel == i)
pos = strlcat(lvl, "\e[1m", sizeof(lvl));
if (i == 0)
@@ -465,7 +526,7 @@ char *runlevel_string(int runlevel, int levels)
else
lvl[pos++] = '0' + i;
if (runlevel == i)
if (!plain && runlevel == i)
pos = strlcat(lvl, "\e[0m", sizeof(lvl));
} else {
lvl[pos++] = '-';
@@ -478,30 +539,96 @@ char *runlevel_string(int runlevel, int levels)
return lvl;
}
static int missing(svc_t *svc)
{
if (svc->state == SVC_HALTED_STATE && svc_is_missing(svc))
return 1;
return 0;
}
static char *svc_command(svc_t *svc, char *buf, size_t len)
{
int bold = missing(svc);
if (plain || whichp(svc->cmd))
bold = 0;
strlcpy(buf, bold ? "\e[1m" : "", len);
strlcat(buf, svc->cmd, len);
for (int i = 1; i < MAX_NUM_SVC_ARGS; i++) {
if (!svc->args[i][0])
break;
strlcat(buf, " ", len);
strlcat(buf, svc->args[i], len);
}
strlcat(buf, bold ? "\e[0m" : "", len);
return buf;
}
static char *svc_environ(svc_t *svc, char *buf, size_t len)
{
int bold = missing(svc);
if (plain || svc_checkenv(svc) || svc->env[0] == '-')
bold = 0;
strlcpy(buf, bold ? "\e[1m" : "", len);
strlcat(buf, svc->env, len);
strlcat(buf, bold ? "\e[0m" : "", len);
return buf;
}
static char *status(svc_t *svc)
{
static char buf[25];
int bold = !plain;
char *s;
s = svc_status(svc);
if (svc->state != SVC_HALTED_STATE)
bold = 0;
snprintf(buf, sizeof(buf), "%s%-8.8s%s",
bold ? "\e[1m" : "", s, bold ? "\e[0m" : "");
return buf;
}
static int show_status(char *arg)
{
char ident[MAX_IDENT_LEN];
char buf[512];
svc_t *svc;
runlevel = runlevel_get(NULL);
if (arg && arg[0]) {
long now = jiffies();
char buf[42] = "N/A";
char uptm[42] = "N/A";
svc = client_svc_find(arg);
if (!svc)
return 1;
printf("Job : %d\n", svc->job);
printf("Service : %s\n", svc->cmd);
printf("Description : %s\n", svc->desc);
printf("Status : %s\n", status(svc));
printf("Identity : %s\n", svc_ident(svc, ident, sizeof(ident)));
printf("Environment : %s\n", svc->env);
printf("Description : %s\n", svc->desc);
printf("Origin : %s\n", svc->file[0] ? svc->file : "built-in");
printf("Environment : %s\n", svc_environ(svc, buf, sizeof(buf)));
printf("Condition(s): %s\n", svc_cond(svc, buf, sizeof(buf)));
printf("Command : %s\n", svc_command(svc, buf, sizeof(buf)));
printf("PID file : %s\n", svc->pidfile);
printf("PID : %d\n", svc->pid);
printf("Uptime : %s\n", svc->pid ? uptime(now - svc->start_time, buf, sizeof(buf)) : buf);
printf("User : %s\n", svc->username);
printf("Group : %s\n", svc->group);
printf("Uptime : %s\n", svc->pid ? uptime(now - svc->start_time, uptm, sizeof(uptm)) : uptm);
printf("Runlevels : %s\n", runlevel_string(runlevel, svc->runlevels));
printf("Status : %s\n", svc_status(svc));
printf("\n");
return do_log(svc->cmd);
@@ -518,21 +645,16 @@ static int show_status(char *arg)
else
strlcat(title, "COMMAND", sizeof(title));
printheader(NULL, title, 0);
print_header(title);
}
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
char *lvls;
int i;
printf("%-*d ", pw, svc->pid);
if (!numeric)
svc_ident(svc, ident, sizeof(ident));
else
svc_jobid(svc, ident, sizeof(ident));
printf("%-*s %-8s ", iw, ident, svc_status(svc));
svc_ident(svc, ident, sizeof(ident));
printf("%-*s %s ", iw, ident, status(svc));
lvls = runlevel_string(runlevel, svc->runlevels);
if (strchr(lvls, '\e'))
@@ -540,91 +662,15 @@ static int show_status(char *arg)
else
printf("%-12.12s ", lvls);
if (!verbose) {
printf("%s\n", svc->desc);
} else {
char args[CMD_SIZE] = "";
for (i = 1; i < MAX_NUM_SVC_ARGS; i++) {
if (strlen(svc->args[i]) == 0)
continue;
if (args[0])
strlcat(args, " ", sizeof(args));
strlcat(args, svc->args[i], sizeof(args));
}
printf("%s %s\n", svc->cmd, args);
}
if (!verbose)
puts(svc->desc);
else
puts(svc_command(svc, buf, sizeof(buf)));
}
return 0;
}
static int dump_cgroup(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf)
{
FILE *fp;
char *group;
char path[256];
char buf[256];
int user = 0;
int num = 0;
if (tflag == FTW_F)
return 0;
snprintf(path, sizeof(path), "%s/cgroup.procs", fpath);
fp = fopen(path, "r");
if (!fp)
return 0;
if (strstr(fpath, "finit/user"))
user = 1;
group = strrchr(fpath, '/');
if (!group)
goto err;
group++;
while (fgets(buf, sizeof(buf), fp)) {
FILE *cfp;
int pid;
if (num == 0)
printf("%c :- %s/", user ? ' ' : '|', group);
num++;
pid = atoi(chomp(buf));
snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
cfp = fopen(path, "r");
if (!cfp)
continue;
fgets(buf, sizeof(buf), cfp);
fclose(cfp);
printf("\n%c :- %d %s", user ? ' ' : '|', pid, buf);
}
err:
fclose(fp);
if (num)
printf("\r%c\n", user ? ' ' : '|');
return 0;
}
static int show_cgroup(char *arg)
{
puts("finit/");
puts("|- init/");
nftw("/sys/fs/cgroup/finit/init", dump_cgroup, 20, 0);
puts("|- system/");
nftw("/sys/fs/cgroup/finit/system", dump_cgroup, 20, 0);
puts("`- user/");
nftw("/sys/fs/cgroup/finit/user", dump_cgroup, 20, 0);
return 0;
}
static int transform(char *nm)
{
char *names[] = {
@@ -669,7 +715,8 @@ static int usage(int rc)
" -b, --batch Batch mode, no screen size probing\n"
" -c, --create Create missing paths (and files) as needed\n"
" -f, --force Ignore missing files and arguments, never prompt\n"
" -n, --numeric Show JOB:ID instead of NAME:ID\n"
" -1, --once Only one lap in commands like 'top'\n"
" -p, --plain Use plain table headings, no ctrl chars\n"
" -t, --no-heading Skip table headings\n"
" -v, --verbose Verbose output\n"
" -h, --help This help text\n"
@@ -685,9 +732,14 @@ static int usage(int rc)
" ls | list List all .conf in " FINIT_RCSD "\n"
" create <CONF> Create .conf in %s\n"
" delete <CONF> Delete .conf in %s\n"
" show <CONF> Show .conf in %s\n"
" edit <CONF> Edit .conf in %s\n"
" touch <CONF> Change .conf in %s\n",
avail, avail, avail, avail);
avail, avail, avail, avail, avail);
else
fprintf(stderr,
" show Show %s\n", FINIT_CONF);
if (has_ena)
fprintf(stderr,
" enable <CONF> Enable .conf in %s\n", avail);
@@ -702,19 +754,22 @@ static int usage(int rc)
" reload Reload " FINIT_CONF " (activate changes)\n");
fprintf(stderr,
// " reload <JOB|NAME>[:ID] Reload (SIGHUP) service by job# or name\n"
// " reload <NAME>[:ID] Reload (SIGHUP) service by name\n"
"\n"
" cond show Show condition status\n"
" cond set <COND> Set (assert) user-defined condition +usr/COND\n"
" cond clear <COND> Clear (deassert) user-defined condition -usr/COND\n"
" cond status Show condition status, default cond command\n"
" cond dump Dump all conditions and their status\n"
"\n"
" log [NAME] Show ten last Finit, or NAME, messages from syslog\n"
" start <JOB|NAME>[:ID] Start service by job# or name, with optional ID\n"
" stop <JOB|NAME>[:ID] Stop/Pause a running service by job# or name\n"
" restart <JOB|NAME>[:ID] Restart (stop/start) service by job# or name\n"
" status <JOB|NAME>[:ID] Show service status, by job# or name\n"
" status | show Show status of services, default command\n"
" start <NAME>[:ID] Start service by name, with optional ID\n"
" stop <NAME>[:ID] Stop/Pause a running service by name\n"
" restart <NAME>[:ID] Restart (stop/start) service by name\n"
" status <NAME>[:ID] Show service status, by name\n"
" status Show status of services, default command\n"
"\n"
" ps List processes based on cgroups\n"
" top Show top-like listing based on cgroups\n"
"\n"
" runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot\n"
" reboot Reboot system\n"
@@ -730,69 +785,128 @@ static int usage(int rc)
else
fprintf(stderr, "\n");
printf("Bug report address: %-40s\n", PACKAGE_BUGREPORT);
#ifdef PACKAGE_URL
printf("Project homepage: %s\n", PACKAGE_URL);
#endif
return rc;
}
static int do_devel(char *arg)
{
(void)arg;
printf("Screen %dx%d\n", screen_cols, screen_rows);
return 0;
}
static int do_help(char *arg)
{
return usage(0);
}
static int cmd_parse(int argc, char *argv[], struct cmd *command)
{
int i, j;
for (i = 0; argc > 0 && command[i].cmd; i++) {
if (!string_match(command[i].cmd, argv[0]))
continue;
if (command[i].ctx)
return cmd_parse(argc - 1, &argv[1], command[i].ctx);
if (command[i].cb) {
int rc = 0;
if (argc == 1)
return command[i].cb(NULL);
for (j = 1; j < argc; j++)
rc |= command[i].cb(argv[j]);
return rc;
}
}
if (argv[0] && strlen(argv[0]) > 0)
errx(1, "No such command. See 'initctl help' for an overview of available commands.");
return command[0].cb(NULL); /* default cmd */
}
int main(int argc, char *argv[])
{
struct command command[] = {
{ "debug", toggle_debug },
{ "help", do_help },
{ "version", show_version },
{ "list", serv_list },
{ "ls", serv_list },
{ "enable", serv_enable },
{ "disable", serv_disable },
{ "touch", serv_touch },
{ "edit", serv_edit },
{ "create", serv_creat },
{ "delete", serv_delete },
{ "reload", do_reload },
{ "cond", do_cond },
{ "log", do_log },
{ "start", do_start },
{ "stop", do_stop },
{ "restart", do_restart },
{ "status", show_status },
{ "show", show_status }, /* Convenience alias */
{ "ps", show_cgroup },
{ "runlevel", do_runlevel },
{ "reboot", do_reboot },
{ "halt", do_halt },
{ "poweroff", do_poweroff },
{ "suspend", do_suspend },
{ "utmp", do_utmp },
{ NULL, NULL }
};
struct option long_options[] = {
{ "batch", 0, NULL, 'b' },
{ "create", 0, NULL, 'c' },
{ "force", 0, NULL, 'f' },
{ "help", 0, NULL, 'h' },
{ "numeric", 0, NULL, 'n' },
{ "once", 0, NULL, '1' },
{ "plain", 0, NULL, 'p' },
{ "no-heading", 0, NULL, 't' },
{ "verbose", 0, NULL, 'v' },
{ NULL, 0, NULL, 0 }
};
struct cmd cond[] = {
{ "status", NULL, do_cond_show }, /* default cmd */
{ "dump", NULL, do_cond_dump },
{ "set", NULL, do_cond_set },
{ "clr", NULL, do_cond_clr },
{ "clear", NULL, do_cond_clr },
{ NULL, NULL, NULL }
};
struct cmd command[] = {
{ "status", NULL, show_status }, /* default cmd */
{ "debug", NULL, toggle_debug },
{ "devel", NULL, do_devel },
{ "help", NULL, do_help },
{ "version", NULL, show_version },
{ "list", NULL, serv_list },
{ "ls", NULL, serv_list },
{ "enable", NULL, serv_enable },
{ "disable", NULL, serv_disable },
{ "touch", NULL, serv_touch },
{ "show", NULL, serv_show },
{ "edit", NULL, serv_edit },
{ "create", NULL, serv_creat },
{ "delete", NULL, serv_delete },
{ "reload", NULL, do_reload },
{ "cond", cond, NULL },
{ "log", NULL, do_log },
{ "start", NULL, do_start },
{ "stop", NULL, do_stop },
{ "restart", NULL, do_restart },
{ "ps", NULL, show_cgroup },
{ "top", NULL, show_cgtop },
{ "runlevel", NULL, do_runlevel },
{ "reboot", NULL, do_reboot },
{ "halt", NULL, do_halt },
{ "poweroff", NULL, do_poweroff },
{ "suspend", NULL, do_suspend },
{ "utmp", NULL, do_utmp },
{ NULL, NULL, NULL }
};
int interactive = 1, c;
char *cmd;
if (transform(progname(argv[0])))
return reboot_main(argc, argv);
while ((c = getopt_long(argc, argv, "bcfh?ntv", long_options, NULL)) != EOF) {
while ((c = getopt_long(argc, argv, "1bcfh?ptv", long_options, NULL)) != EOF) {
switch(c) {
case '1':
ionce = 1;
break;
case 'b':
interactive = 0;
break;
@@ -809,8 +923,8 @@ int main(int argc, char *argv[])
case '?':
return usage(0);
case 'n':
numeric = 1;
case 'p':
plain = 1;
break;
case 't':
@@ -826,10 +940,8 @@ int main(int argc, char *argv[])
if (interactive)
screen_init();
if (optind >= argc)
return show_status(NULL);
if (!has_utmp()) {
/* system w/o utmp support, disable 'utmp' command */
for (c = 0; command[c].cmd; c++) {
if (!string_compare(command[c].cmd, "utmp"))
continue;
@@ -839,26 +951,7 @@ int main(int argc, char *argv[])
}
}
cmd = argv[optind++];
for (c = 0; command[c].cmd; c++) {
int rc = 0;
if (!string_match(command[c].cmd, cmd))
continue;
if (!command[c].cb)
continue;
/* no arg, just a command, e.g. initctl ls */
if (optind >= argc)
return command[c].cb(NULL);
/* >=1 arg, e.g., initctl del foo bar baz */
while (optind < argc)
rc |= command[c].cb(argv[optind++]);
return rc;
}
return usage(1);
return cmd_parse(argc - optind, &argv[optind], command);
}
void logit(int prio, const char *fmt, ...)
+46
View File
@@ -0,0 +1,46 @@
/* New client tool, replaces old /dev/initctl API and telinit tool
*
* Copyright (c) 2015-2021 Joachim Wiberg <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef FINIT_INITCTL_H_
#define FINIT_INITCTL_H_
#include "finit.h"
#include "util.h"
extern int icreate; /* initctl -c */
extern int iforce; /* initctl -f */
extern int ionce; /* initctl -1 */
extern int heading; /* initctl -t */
extern int verbose; /* initctl -v */
extern int plain; /* initctl -p */
extern void print_header(const char *fmt, ...);
#endif /* FINIT_INITCTL_H_ */
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+4 -7
View File
@@ -22,7 +22,6 @@
*/
#include "config.h"
#include <sched.h> /* sched_yield() */
#include <stdio.h>
#include <stdarg.h>
#include <lite/lite.h>
@@ -50,15 +49,13 @@ void log_exit(void)
* Unless in debug mode at shutdown, Reinitialize screen,
* terminal may have been resized at runtime
*/
if (debug)
screen_exit();
else
if (!debug)
screen_init();
enable_progress(1);
}
void log_open(void)
static void log_open(void)
{
int opts = LOG_PID;
@@ -79,7 +76,6 @@ void log_debug(void)
if (debug) {
loglevel = LOG_DEBUG;
screen_exit();
} else {
loglevel = LOG_NOTICE;
screen_init();
@@ -103,11 +99,12 @@ void logit(int prio, const char *fmt, ...)
if (up || fexist("/dev/log")) {
if (!up)
log_open();
vsyslog(prio, fmt, ap);
goto done;
}
if (!debug && prio > loglevel)
if (prio > loglevel)
goto done;
fp = fopen("/dev/kmsg", "w");
+2 -65
View File
@@ -30,75 +30,12 @@
#include <syslog.h>
#include <unistd.h>
#include <sys/stat.h>
#include <lite/lite.h>
static const char version_info[] = PACKAGE_NAME " v" PACKAGE_VERSION;
extern int logrotate(char *file, int num, off_t sz);
static int create(char *path, mode_t mode, uid_t uid, gid_t gid)
{
return mknod(path, S_IFREG | mode, 0) || chown(path, uid, gid);
}
/*
* This function triggers a log rotates of @file when size >= @sz bytes
* At most @num old versions are kept and by default it starts gzipping
* .2 and older log files. If gzip is not available in $PATH then @num
* files are kept uncompressed.
*/
static int logrotate(char *file, int num, off_t sz)
{
int cnt;
struct stat st;
if (stat(file, &st))
return 1;
if (sz > 0 && S_ISREG(st.st_mode) && st.st_size > sz) {
if (num > 0) {
size_t len = strlen(file) + 10 + 1;
char ofile[len];
char nfile[len];
/* First age zipped log files */
for (cnt = num; cnt > 2; cnt--) {
snprintf(ofile, len, "%s.%d.gz", file, cnt - 1);
snprintf(nfile, len, "%s.%d.gz", file, cnt);
/* May fail because ofile doesn't exist yet, ignore. */
(void)rename(ofile, nfile);
}
for (cnt = num; cnt > 0; cnt--) {
snprintf(ofile, len, "%s.%d", file, cnt - 1);
snprintf(nfile, len, "%s.%d", file, cnt);
/* May fail because ofile doesn't exist yet, ignore. */
(void)rename(ofile, nfile);
if (cnt == 2 && !access(nfile, F_OK)) {
size_t len = 5 + strlen(nfile) + 1;
char cmd[len];
snprintf(cmd, len, "gzip %s", nfile);
system(cmd);
remove(nfile);
}
}
if (rename(file, nfile))
(void)truncate(file, 0);
else
create(file, st.st_mode, st.st_uid, st.st_gid);
} else {
if (truncate(file, 0))
syslog(LOG_ERR | LOG_PERROR, "Failed truncating %s during logrotate: %s", file, strerror(errno));
}
}
return 0;
}
static int checksz(FILE *fp, off_t sz)
{
struct stat st;
+102
View File
@@ -0,0 +1,102 @@
/*
* Copyright (c) 2018-2021 Joachim Wiberg <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <lite/lite.h>
#include <stdio.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
static int recreate(char *path, mode_t mode, uid_t uid, gid_t gid)
{
if (mknod(path, S_IFREG | mode, 0) || chown(path, uid, gid)) {
syslog(LOG_ERR, "Failed recreating %s: %s", path, strerror(errno));
return -1;
}
return 0;
}
/*
* This function triggers a log rotates of @file when size >= @sz bytes
* At most @num old versions are kept and by default it starts gzipping
* .2 and older log files. If gzip is not available in $PATH then @num
* files are kept uncompressed.
*/
int logrotate(char *file, int num, off_t sz)
{
int cnt;
struct stat st;
if (stat(file, &st))
return 1;
if (sz > 0 && S_ISREG(st.st_mode) && st.st_size > sz) {
if (num > 0) {
size_t len = strlen(file) + 10 + 1;
char ofile[len];
char nfile[len];
/* First age zipped log files */
for (cnt = num; cnt > 2; cnt--) {
snprintf(ofile, len, "%s.%d.gz", file, cnt - 1);
snprintf(nfile, len, "%s.%d.gz", file, cnt);
/* May fail because ofile doesn't exist yet, ignore. */
if (rename(ofile, nfile) && errno != ENOENT)
syslog(LOG_ERR, "Failed logrotate %s: %s",
ofile, strerror(errno));
}
for (cnt = num; cnt > 0; cnt--) {
snprintf(ofile, len, "%s.%d", file, cnt - 1);
snprintf(nfile, len, "%s.%d", file, cnt);
/* May fail because ofile doesn't exist yet, ignore. */
if (rename(ofile, nfile) && errno != ENOENT) {
syslog(LOG_ERR, "Failed logrotate %s: %s",
ofile, strerror(errno));
continue;
}
if (cnt == 2 && fexist(nfile)) {
if (systemf("gzip %s 2>/dev/null", nfile))
continue; /* no gzip, probably */
(void)remove(nfile);
}
}
if (rename(file, nfile))
goto fallback;
recreate(file, st.st_mode, st.st_uid, st.st_gid);
} else {
fallback:
if (truncate(file, 0))
syslog(LOG_ERR, "Failed truncating %s during logrotate: %s",
file, strerror(errno));
}
}
return 0;
}
+10 -12
View File
@@ -95,18 +95,15 @@ char *pid_get_name(pid_t pid, char *name, size_t len)
char *pid_file(svc_t *svc)
{
static char path[256];
char fn[MAX_ARG_LEN];
if (svc->pidfile[0]) {
if (svc->pidfile[0] == '!')
return &svc->pidfile[1];
return svc->pidfile;
}
snprintf(fn, sizeof(fn), "%s%s.pid", _PATH_VARRUN, basename(svc->cmd));
errno = ENOENT;
return pid_runpath(fn, path, sizeof(path));
return NULL;
}
pid_t pid_file_read(const char *fn)
@@ -143,12 +140,13 @@ int pid_file_create(svc_t *svc)
return fclose(fp);
}
static int pid_realpath(svc_t *svc, char *file)
int pid_file_set(svc_t *svc, char *file, int not)
{
int not = 0;
if (!file)
if (!file) {
file = pid_file(svc);
if (!file)
return -1;
}
if (file[0] == '!') {
not = 1;
@@ -202,7 +200,7 @@ int pid_file_parse(svc_t *svc, char *arg)
arg += 4;
if ((arg[0] == '!' && arg[1] == '/') || arg[0] == '/')
return pid_realpath(svc, arg);
return pid_file_set(svc, arg, 0);
if (arg[0] == '!') {
arg++;
@@ -213,12 +211,12 @@ int pid_file_parse(svc_t *svc, char *arg)
if (len > 4 && strcmp(&path[len - 4], ".pid"))
strlcat(path, ".pid", sizeof(path));
return pid_realpath(svc, path);
return pid_file_set(svc, path, 0);
}
/* 'pid' arg, no argument following */
if (!strcmp(arg, "pid"))
return pid_realpath(svc, NULL);
return pid_file_set(svc, NULL, 0);
return 1;
}
+1
View File
@@ -32,6 +32,7 @@ int pid_alive (pid_t pid);
char *pid_get_name (pid_t pid, char *name, size_t len);
char *pid_file (svc_t *svc);
int pid_file_set (svc_t *svc, char *file, int not);
pid_t pid_file_read (const char *fn);
int pid_file_create (svc_t *svc);
int pid_file_parse (svc_t *svc, char *arg);
+48 -11
View File
@@ -30,10 +30,7 @@
#include <stdlib.h>
#include <lite/lite.h>
#include "util.h"
extern int icreate; /* initctl -c */
extern int iforce; /* initctl -f */
#include "initctl.h"
static int calc_width(char *arr[], size_t len)
@@ -53,20 +50,40 @@ static int calc_width(char *arr[], size_t len)
static void do_list(const char *path)
{
char buf[screen_cols];
static int once = 0;
int width, num, prev;
glob_t gl;
size_t i;
chdir(path);
if (chdir(path))
return;
if (glob("*.conf", 0, NULL, &gl))
return;
if (gl.gl_pathc <= 0)
goto done;
snprintf(buf, sizeof(buf), "%s ", path);
printheader(NULL, buf, 0);
if (plain) {
if (heading)
print_header("%s%s ", once ? "\n" : "", path);
once++;
for (i = 0; i < gl.gl_pathc; i++) {
if (!heading) {
char buf[512];
paste(buf, sizeof(buf), path, gl.gl_pathv[i]);
puts(buf);
} else
puts(gl.gl_pathv[i]);
}
goto done;
}
if (heading)
print_header("%s ", path);
width = calc_width(gl.gl_pathv, gl.gl_pathc);
if (width <= 0)
@@ -138,6 +155,11 @@ static char *conf(char *path, size_t len, char *name, int creat)
{
char corr[40];
if (!name || !name[0]) {
strlcpy(path, FINIT_CONF, len);
return path;
}
if (!strstr(name, ".conf")) {
snprintf(corr, sizeof(corr), "%s.conf", name);
name = corr;
@@ -208,7 +230,7 @@ int do_disable(char *arg, int check)
if (chdir(FINIT_RCSD))
err(1, "failed cd %s", FINIT_RCSD);
chdir("enabled"); /* System *may* have enabled/ dir. */
(void)chdir("enabled"); /* System *may* have enabled/ dir. */
if (check && stat(arg, &st))
errx(1, "%s not (an) enabled (service).", arg);
@@ -250,6 +272,20 @@ int serv_touch(char *arg)
return 0;
}
int serv_show(char *arg)
{
char path[256];
char *fn;
fn = conf(path, sizeof(path), arg, 0);
if (!fexist(fn)) {
warnx("Cannot find %s", arg);
return 1;
}
return systemf("cat %s", fn);
}
/*
* Try to open an editor for the given file, if creat is given we
* create a new file based on /lib/finit/sample.conf
@@ -270,7 +306,7 @@ static int do_edit(char *arg, int creat)
char *editor[] = {
"sensible-editor",
"editor",
"${VISUAL:-${EDITOR:-$(command -v mg vi | head -1)}}"
"${VISUAL:-${EDITOR:-$(command -v mg || command -v vi)}}"
};
char path[256];
char *fn;
@@ -358,7 +394,8 @@ int serv_delete(char *arg)
if (iforce || yorn("Remove file and symlink(s) to %s (y/N)? ", fn)) {
do_disable(arg, 0);
remove(fn);
if (remove(fn))
err(1, "Failed removing %s", fn);
}
return 0;
+1
View File
@@ -28,6 +28,7 @@ int serv_list (char *arg);
int serv_enable (char *arg);
int serv_disable (char *arg);
int serv_touch (char *arg);
int serv_show (char *arg);
int serv_edit (char *arg);
int serv_creat (char *arg);
int serv_delete (char *arg);
+130 -53
View File
@@ -49,8 +49,6 @@
#include "utmp-api.h"
#include "schedule.h"
#define RESPAWN_MAX 10 /* Prevent endless respawn of faulty services. */
static struct wq work = {
.cb = service_worker,
};
@@ -262,37 +260,6 @@ static int redirect(svc_t *svc)
return 0;
}
/*
* non-zero env, no checking if file exists or not
*/
static char *svc_getenv(svc_t *svc)
{
int v = 0;
if (!svc->env[0])
return NULL;
if (svc->env[0] == '-')
v = 1;
return &svc->env[v];
}
/*
* Check if svc has env, if env file exists returns true
* if it doesn't exist, but has '-', also true. if svc
* doesn't have env, also true.
*/
static int check_env(svc_t *svc)
{
char *env = svc_getenv(svc);
if (!env)
return 1;
return fexist(env);
}
/*
* Source environment file, if it exists
* Note: must be called from privsepped child
@@ -309,7 +276,7 @@ static void source_env(svc_t *svc)
if (!fn)
return;
/* Warning in service_start() after check_env() */
/* Warning in service_start() after svc_checkenv() */
fp = fopen(fn, "r");
if (!fp)
return;
@@ -384,6 +351,30 @@ static int is_norespawn(void)
fexist("/tmp/norespawn");
}
/* used for process group name, derived from originating filename,
* so to group multiple services, place them in the same .conf
*/
static char *group_name(svc_t *svc, char *buf, size_t len)
{
char *ptr;
if (!svc->file[0])
return svc_ident(svc, buf, len);
ptr = strrchr(svc->file, '/');
if (ptr)
ptr++;
else
ptr = svc->file;
strlcpy(buf, ptr, len);
ptr = strstr(buf, ".conf");
if (ptr)
*ptr = 0;
return buf;
}
/**
* service_start - Start service
* @svc: Service to start
@@ -395,6 +386,7 @@ static int service_start(svc_t *svc)
{
int result = 0, do_progress = 1;
sigset_t nmask, omask;
char grnam[80];
pid_t pid;
size_t i;
@@ -407,14 +399,15 @@ static int service_start(svc_t *svc)
/* Don't try and start service if it doesn't exist. */
if (!whichp(svc->cmd)) {
print(1, "%s: does not exist", svc->cmd);
logit(LOG_WARNING, "%s: missing or not in $PATH", svc->cmd);
svc_missing(svc);
return 1;
}
/* Unlike systemd we do not allow starting service if env is missing, unless - */
if (!check_env(svc)) {
print(1, "%s: environment file %s does not exist (yet)", svc->cmd, svc->env);
if (!svc_checkenv(svc)) {
logit(LOG_WARNING, "%s: missing env file %s", svc->cmd, svc->env);
svc_missing(svc);
return 1;
}
@@ -440,8 +433,6 @@ static int service_start(svc_t *svc)
sigprocmask(SIG_BLOCK, &nmask, &omask);
pid = fork();
cgroup_service(svc->name, svc->id, pid);
if (pid == 0) {
int status;
char *home = NULL;
@@ -464,17 +455,18 @@ static int service_start(svc_t *svc)
/* Set desired user+group */
if (gid >= 0)
setgid(gid);
(void)setgid(gid);
if (uid >= 0) {
setuid(uid);
(void)setuid(uid);
/* Set default path for regular users */
if (uid > 0)
setenv("PATH", _PATH_DEFPATH, 1);
if (home) {
setenv("HOME", home, 1);
chdir(home);
if (chdir(home))
(void)chdir("/");
}
}
@@ -578,6 +570,8 @@ static int service_start(svc_t *svc)
_d("Starting %s %s", svc->cmd, buf);
}
cgroup_service(group_name(svc, grnam, sizeof(grnam)), pid);
logit(LOG_CONSOLE | LOG_NOTICE, "Starting %s[%d]", svc_ident(svc, NULL, 0), pid);
svc->pid = pid;
@@ -647,11 +641,12 @@ static void service_cleanup(svc_t *svc)
char *fn;
fn = pid_file(svc);
if (remove(fn) && errno != ENOENT)
if (fn && remove(fn) && errno != ENOENT)
logit(LOG_CRIT, "Failed removing service %s pidfile %s",
basename(svc->cmd), fn);
/* No longer running, update books. */
svc->oldpid = svc->pid;
svc->start_time = svc->pid = 0;
}
@@ -918,24 +913,56 @@ static void parse_name(svc_t *svc, char *arg)
*/
static void parse_cmdline_args(svc_t *svc, char *cmd)
{
int i;
char sep = 0;
char *arg;
int i = 0;
strlcpy(svc->args[0], cmd, sizeof(svc->args[0]));
strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
while (i < MAX_NUM_SVC_ARGS)
svc->args[i++][0] = 0;
/*
* Copy supplied args. Stop at MAX_NUM_SVC_ARGS-1 to allow the args
* array to be zero-terminated.
*/
for (i = 1; (arg = strtok(NULL, " ")) && i < (MAX_NUM_SVC_ARGS - 1); i++)
strlcpy(svc->args[i], arg, sizeof(svc->args[0]));
for (i = 1; (arg = strtok(NULL, " ")) && i < (MAX_NUM_SVC_ARGS - 1);) {
char ch = arg[0];
size_t len;
/* XXX: ugly string arg re-concatenation, fixme */
if (ch == '"' || ch == '\'')
sep = ch;
else if (sep)
strlcat(svc->args[i], " ", sizeof(svc->args[0]));
strlcat(svc->args[i], arg, sizeof(svc->args[0]));
/* string arg contained already? */
len = strlen(arg);
if (sep && len >= 1) {
ch = arg[len - 1];
if (ch != sep)
continue;
}
sep = 0;
i++;
}
/*
* Clear remaining args in case they were set earlier.
* This also zero-terminates the args array.
*/
for (; i < MAX_NUM_SVC_ARGS; i++)
svc->args[i][0] = 0;
while (i < MAX_NUM_SVC_ARGS)
svc->args[i++][0] = 0;
#if 0
for (i = 0; i < MAX_NUM_SVC_ARGS; i++) {
if (!svc->args[i][0])
break;
_d("%s ", svc->args[i]);
}
#endif
}
@@ -1094,6 +1121,9 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
if (type == SVC_TYPE_SERVICE && manual)
svc_stop(svc);
} else {
/* e.g., if missing cmd or env before */
svc_unblock(svc);
}
/* Always clear svc PID file, for now. See TODO */
@@ -1110,6 +1140,9 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
strlcpy(svc->group, ptr, sizeof(svc->group));
}
strlcpy(svc->username, username, sizeof(svc->username));
} else {
getcuser(svc->username, sizeof(svc->username));
getcgroup(svc->group, sizeof(svc->group));
}
parse_cmdline_args(svc, cmd);
@@ -1130,6 +1163,8 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
strlcpy(svc->desc, desc, sizeof(svc->desc));
if (env)
parse_env(svc, env);
if (file)
strlcpy(svc->file, file, sizeof(svc->file));
/* Set configured limits */
memcpy(svc->rlimit, rlimit, sizeof(svc->rlimit));
@@ -1194,6 +1229,7 @@ void service_monitor(pid_t lost, int status)
if (svc_is_daemon(svc)) {
service_cleanup(svc);
} else if (svc_is_runtask(svc)) {
/* run/task should run at least once per runlevel */
if (WIFEXITED(status) && !WEXITSTATUS(status))
svc->started = 1;
else
@@ -1215,6 +1251,38 @@ void service_monitor(pid_t lost, int status)
sm_step(&sm);
}
static void svc_mark_affected(char *cond)
{
svc_t *svc, *iter = NULL;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (!svc_has_cond(svc))
continue;
if (cond_affects(cond, svc->cond))
svc_mark_dirty(svc);
}
}
/*
* Called on conf_reload() to update service reverse dependencies.
* E.g., if ospfd depends on zebra and the zebra Finit conf has
* changed, we need to mark the ospfd Finit conf as changed too.
*/
void service_update_rdeps(void)
{
svc_t *svc, *iter = NULL;
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
char cond[MAX_COND_LEN];
if (!svc_is_changed(svc))
continue;
svc_mark_affected(mkcond(svc, cond, sizeof(cond)));
}
}
static void service_retry(svc_t *svc)
{
int timeout;
@@ -1229,7 +1297,7 @@ static void service_retry(svc_t *svc)
return;
}
if (*restart_cnt >= RESPAWN_MAX) {
if (*restart_cnt >= SVC_RESPAWN_MAX) {
logit(LOG_CONSOLE | LOG_WARNING, "Service %s keeps crashing, not restarting.",
svc_ident(svc, NULL, 0));
svc_crashing(svc);
@@ -1242,12 +1310,12 @@ static void service_retry(svc_t *svc)
_d("%s crashed, trying to start it again, attempt %d", svc->cmd, *restart_cnt);
logit(LOG_CONSOLE | LOG_WARNING, "Service %s[%d] died, restarting (%d/%d)",
svc_ident(svc, NULL, 0), svc->pid, *restart_cnt, RESPAWN_MAX);
svc_ident(svc, NULL, 0), svc->oldpid, *restart_cnt, SVC_RESPAWN_MAX);
svc_unblock(svc);
service_step(svc);
/* Wait 2s for the first 5 respawns, then back off to 5s */
timeout = ((*restart_cnt) <= (RESPAWN_MAX / 2)) ? 2000 : 5000;
timeout = ((*restart_cnt) <= (SVC_RESPAWN_MAX / 2)) ? 2000 : 5000;
service_timeout_after(svc, timeout, service_retry);
}
@@ -1301,10 +1369,15 @@ restart:
case SVC_STOPPING_STATE:
if (!svc->pid) {
char cond[MAX_COND_LEN];
/* PID was collected normally, no need to kill it */
_d("%s: Stopped normally, no need to send SIGKILL :)", svc->cmd);
_d("%s: stopped normally, no need to send SIGKILL :)", svc->cmd);
service_timeout_cancel(svc);
_d("%s: clearing pid condition ...", svc->name);
cond_clear(mkcond(svc, cond, sizeof(cond)));
switch (svc->type) {
case SVC_TYPE_SERVICE:
svc_set_state(svc, SVC_HALTED_STATE);
@@ -1333,6 +1406,10 @@ restart:
err = service_start(svc);
if (err) {
if (svc_is_missing(svc)) {
svc_set_state(svc, SVC_HALTED_STATE);
break;
}
(*restart_cnt)++;
break;
}
+1
View File
@@ -33,6 +33,7 @@ void service_unregister (svc_t *svc);
void service_runtask_clean (void);
void service_reload_dynamic (void);
void service_update_rdeps (void);
int service_step (svc_t *svc);
void service_step_all (int types);
+9 -9
View File
@@ -192,10 +192,10 @@ void do_kill(int signo)
if (fgets(file, sizeof(file), fp)) {
if (strstr(file, "gdbserver"))
_d("Skipping %s ...", file);
else if (file[0] != '@')
kill(pid, signo);
else
else if (file[0] == '@')
_d("Skipping %s ...", &file[1]);
else
kill(pid, signo);
}
fclose(fp);
}
@@ -214,11 +214,11 @@ void do_shutdown(shutop_t op)
utmp_set_halt();
/*
* Tell all remaining non-monitored processes to exit, give them
* some time to exit gracefully, 2 sec is customary.
* Tell remaining non-monitored processes to exit, give them
* time to exit gracefully, 2 sec was customary, we go for 1.
*/
do_kill(SIGTERM);
do_sleep(2);
do_sleep(1);
do_kill(SIGKILL);
/* Exit plugins and API gracefully */
@@ -274,15 +274,15 @@ void do_shutdown(shutop_t op)
do_sleep(1);
}
_e("Rebooting ...");
_d("Rebooting ...");
reboot(RB_AUTOBOOT);
} else if (op == SHUT_OFF) {
_e("Powering down ...");
_d("Powering down ...");
reboot(RB_POWER_OFF);
}
/* Also fallback if any of the other two fails */
_e("Halting ...");
_d("Halting ...");
reboot(RB_HALT_SYSTEM);
}
+5 -3
View File
@@ -156,9 +156,11 @@ restart:
/* Disable login in single-user mode as well as shutdown/reboot */
nologin();
/* Make sure to (re)load all *.conf in /etc/finit.d/ */
if (conf_any_change())
conf_reload();
if (runlevel != 0 && runlevel != 6) {
/* Make sure to (re)load all *.conf in /etc/finit.d/ */
if (conf_any_change())
conf_reload();
}
/* Reset once flag of runtasks */
service_runtask_clean();
+16 -1
View File
@@ -415,8 +415,20 @@ svc_t *svc_find_by_pidfile(char *fn)
pid_t pid;
pid = pid_file_read(fn);
if (pid == -1)
if (pid <= 0) {
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
int v = 0;
if (svc->pidfile[0] == '!')
v = 1;
if (strcmp(&svc->pidfile[v], fn))
continue;
return svc;
}
return NULL;
}
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
if (svc->pid != pid)
@@ -532,6 +544,9 @@ int svc_enabled(svc_t *svc)
if (svc_is_removed(svc) || svc_is_blocked(svc))
return 0;
if (svc_is_missing(svc))
return 0;
return 1;
}
+66 -7
View File
@@ -77,6 +77,9 @@ typedef enum {
/* Default kill delay (msec) after SIGTERM (svc->sighalt) that we SIGKILL processes */
#define SVC_TERM_TIMEOUT 3000
/* Prevent endless respawn of faulty services. */
#define SVC_RESPAWN_MAX 10
/*
* Default enable for all services, can be stopped by means
* of issuing an initctl call. E.g.
@@ -86,9 +89,8 @@ typedef enum {
typedef struct svc {
TAILQ_ENTRY(svc) link;
/* Instance specifics */
int job; /* JOB: */
char id[MAX_ID_LEN]; /* :ID */
/* Origin of service */
char file[MAX_ARG_LEN];
/* Limits and scoping */
struct rlimit rlimit[RLIMIT_NLIMITS];
@@ -96,7 +98,7 @@ typedef struct svc {
/* Service details */
int sighalt; /* Signal to stop prorcess, default: SIGTERM */
int killdelay; /* Delay in msec before sending SIGKILL */
pid_t pid;
pid_t oldpid, pid;
char pidfile[256];
long start_time; /* Start time, as seconds since boot, from sysinfo() */
int started; /* Set for run/task/sysv to track if started */
@@ -110,7 +112,11 @@ typedef struct svc {
int sighup; /* This service supports SIGHUP :) */
svc_block_t block; /* Reason that this service is currently stopped */
char cond[MAX_COND_LEN];
/* Instance specifics */
int job; /* For intenal use only, canonical ref is NAME:ID */
char name[MAX_ARG_LEN];
char id[MAX_ID_LEN]; /* :ID */
/* Counters */
char once; /* run/task, (at least) once per runlevel */
@@ -191,12 +197,14 @@ static inline void svc_starting (svc_t *svc) { if (svc) svc->starting = 1;
static inline void svc_started (svc_t *svc) { if (svc) svc->starting = 0; }
static inline int svc_is_starting (svc_t *svc) { return svc && 0 != svc->starting; }
static inline int svc_is_removed (svc_t *svc) { return svc && -1 == svc->dirty; }
static inline int svc_is_changed (svc_t *svc) { return svc && 0 != svc->dirty; }
static inline int svc_is_updated (svc_t *svc) { return svc && 1 == svc->dirty; }
static inline int svc_is_removed (svc_t *svc) { return svc && -1 == svc->dirty; }
static inline int svc_is_changed (svc_t *svc) { return svc && 0 != svc->dirty; }
static inline int svc_is_updated (svc_t *svc) { return svc && 1 == svc->dirty; }
static inline int svc_is_blocked (svc_t *svc) { return svc && svc->block != SVC_BLOCK_NONE; }
static inline int svc_is_busy (svc_t *svc) { return svc && svc->block == SVC_BLOCK_BUSY; }
static inline int svc_is_missing (svc_t *svc) { return svc && svc->block == SVC_BLOCK_MISSING; }
static inline void svc_unblock (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_NONE; }
#define svc_start(svc) svc_unblock(svc)
static inline void svc_stop (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_USER; }
@@ -205,6 +213,26 @@ static inline void svc_missing (svc_t *svc) { if (svc) svc->block = SVC_BLOC
static inline void svc_restarting (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_RESTARTING; }
static inline void svc_crashing (svc_t *svc) { if (svc) svc->block = SVC_BLOCK_CRASHING; }
/* Has condition in configuration and cond is allowed? */
static inline int svc_has_cond(svc_t *svc)
{
if (!svc->cond[0])
return 0;
switch (svc->type) {
case SVC_TYPE_SERVICE:
case SVC_TYPE_TASK:
case SVC_TYPE_RUN:
case SVC_TYPE_SYSV:
return 1;
default:
break;
}
return 0;
}
static inline char *svc_status(svc_t *svc)
{
if (!svc)
@@ -324,6 +352,37 @@ static inline char *svc_jobid(svc_t *svc, char *buf, size_t len)
return buf;
}
/*
* non-zero env, no checking if file exists or not
*/
static inline char *svc_getenv(svc_t *svc)
{
int v = 0;
if (!svc->env[0])
return NULL;
if (svc->env[0] == '-')
v = 1;
return &svc->env[v];
}
/*
* Check if svc has env, if env file exists returns true
* if it doesn't exist, but has '-', also true. if svc
* doesn't have env, also true.
*/
static inline int svc_checkenv(svc_t *svc)
{
char *env = svc_getenv(svc);
if (!env)
return 1;
return fexist(env);
}
#endif /* FINIT_SVC_H_ */
/**
-1
View File
@@ -30,7 +30,6 @@
#include <lite/queue.h> /* BSD sys/queue.h API */
#define TTY_MAX_ARGS 16
#define EVENT_SIZE ((sizeof(struct inotify_event) + NAME_MAX + 1))
struct tty {
LIST_ENTRY(tty) link;
+62 -15
View File
@@ -28,9 +28,15 @@
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#ifdef HAVE_TERMIOS_H
# include <termios.h>
#endif
#include <time.h>
#include <unistd.h>
#include <utmp.h>
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#include <sys/sysinfo.h> /* sysinfo() */
#include <lite/lite.h> /* strlcat() */
#include "util.h"
@@ -202,25 +208,66 @@ char *sanitize(char *arg, size_t len)
}
/*
* Called at boot, and shutdown, to (re)initialize the
* screen size for print() et al.
* Called by initctl, and by finit at boot and shutdown, to
* (re)initialize the screen size for print() et al.
*/
void screen_init(void)
int screen_init(void)
{
if (!isatty(STDOUT_FILENO))
return;
#ifdef HAVE_TERMIOS_H
struct pollfd fd = { STDIN_FILENO, POLLIN, 0 };
struct winsize ws = { 0 };
struct termios tc, saved;
initscr(&screen_rows, &screen_cols);
}
/*
* Basic TTY init, CLOCaL is important or TIOCWINSZ will block
* until DCD is asserted, and we won't ever get it.
*/
tcgetattr(STDERR_FILENO, &tc);
saved = tc;
tc.c_cflag |= (CLOCAL | CREAD);
tc.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
tcsetattr(STDERR_FILENO, TCSANOW, &tc);
/*
* Called when debug mode is enabled to revert back
* to old-school safe defaults.
*/
void screen_exit(void)
{
screen_rows = 24;
screen_cols = 80;
/* 1. Try TIOCWINSZ to query window size from kernel */
if (!ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws)) {
screen_rows = ws.ws_row;
screen_cols = ws.ws_col;
/* Likely doesn't work in PID 1 after kernel starts us ... */
if (!ws.ws_row && !ws.ws_col)
goto fallback;
} else if (!isatty(STDOUT_FILENO)) {
/* 2. We may be running under watch(1) */
screen_cols = atonum(getenv("COLUMNS"));
screen_rows = atonum(getenv("LINES"));
} else {
fallback:
/* 3. ANSI goto + query cursor position trick as fallback */
fprintf(stderr, "\e7\e[r\e[999;999H\e[6n");
if (poll(&fd, 1, 300) > 0) {
int row, col;
if (scanf("\e[%d;%dR", &row, &col) == 2) {
screen_cols = col;
screen_rows = row;
}
}
/* Jump back to where we started (\e7) */
fprintf(stderr, "\e8");
}
tcsetattr(STDERR_FILENO, TCSANOW, &saved);
/* Sanity check */
if (screen_cols <= 0)
screen_cols = 80;
if (screen_rows <= 0)
screen_rows = 24;
#endif
return screen_cols;
}
/**
+1 -3
View File
@@ -55,9 +55,7 @@ char *uptime (long secs, char *buf, size_t len);
char *sanitize (char *arg, size_t len);
void screen_init (void);
void screen_exit (void);
int screen_width (void);
int screen_init (void);
static inline char *strterm(char *str, size_t len)
{
+5 -61
View File
@@ -35,6 +35,9 @@
#define MAX_NO 5
#define MAX_SZ 100 * 1024
extern int logrotate(char *file, int num, off_t sz);
static void utmp_strncpy(char *dst, const char *src, size_t dlen)
{
size_t i;
@@ -46,65 +49,6 @@ static void utmp_strncpy(char *dst, const char *src, size_t dlen)
dst[i] = 0;
}
#ifdef LOGROTATE_ENABLED
/*
* This function triggers a log rotates of @file when size >= @sz bytes
* At most @num old versions are kept and by default it starts gzipping
* .2 and older log files. If gzip is not available in $PATH then @num
* files are kept uncompressed.
*/
static int logrotate(char *file, int num, off_t sz)
{
int cnt;
struct stat st;
if (stat(file, &st))
return 1;
if (sz > 0 && S_ISREG(st.st_mode) && st.st_size > sz) {
if (num > 0) {
size_t len = strlen(file) + 10 + 1;
char ofile[len];
char nfile[len];
/* First age zipped log files */
for (cnt = num; cnt > 2; cnt--) {
snprintf(ofile, len, "%s.%d.gz", file, cnt - 1);
snprintf(nfile, len, "%s.%d.gz", file, cnt);
/* May fail because ofile doesn't exist yet, ignore. */
(void)rename(ofile, nfile);
}
for (cnt = num; cnt > 0; cnt--) {
snprintf(ofile, len, "%s.%d", file, cnt - 1);
snprintf(nfile, len, "%s.%d", file, cnt);
/* May fail because ofile doesn't exist yet, ignore. */
(void)rename(ofile, nfile);
if (cnt == 2 && fexist(nfile)) {
size_t len = 5 + strlen(nfile) + 1;
char cmd[len];
snprintf(cmd, len, "gzip %s", nfile);
run(cmd);
remove(nfile);
}
}
(void)rename(file, nfile);
create(file, st.st_mode, st.st_uid, st.st_gid);
} else {
truncate(file, 0);
}
}
return 0;
}
#endif /* LOGROTATE_ENABLED */
/*
* Rotate /var/log/wtmp (+ btmp?) and /run/utmp
*
@@ -194,13 +138,13 @@ static int set_getty(int type, char *tty, char *id, char *user)
return 0;
if (!tty)
line = "";
line = NULL;
else if (!strncmp(tty, "/dev/", 5))
line = tty + 5;
else
line = tty;
if (!id) {
if (!id && line) {
id = line;
if (!strncmp(id, "pts/", 4))
id += 4;