Skip to content

Commit

Permalink
autostart
Browse files Browse the repository at this point in the history
  • Loading branch information
navi-desu committed Feb 6, 2025
1 parent 7a608db commit 7619c1e
Show file tree
Hide file tree
Showing 5 changed files with 271 additions and 0 deletions.
31 changes: 31 additions & 0 deletions init.d/users.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!@SBINDIR@/openrc-run
# Copyright (c) 2017 The OpenRC Authors.
# See the Authors file at the top-level directory of this distribution and
# https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS
#
# This file is part of OpenRC. It is subject to the license terms in
# the LICENSE file found in the top-level directory of this
# distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE
# This file may not be copied, modified, propagated, or distributed
# except according to the terms contained in the LICENSE file.

description="Starts and stops openrc --user for system users."

start() {
mkdir "$RC_SVCDIR"/users || return 1
for user in ${rc_users}; do
ebegin "Starting user session for $user"
openrc-user $user start
eend $?
done
return 0
}

stop() {
for user in "$RC_SVCDIR"/users/*; do
ebegin "Stopping user session for $user"
openrc-user ${user##*/} shutdown
eend $?
done
return 0
}
22 changes: 22 additions & 0 deletions sh/openrc-user.sh.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!@SHELL@

. @LIBEXECDIR@/sh/functions.sh

_sysconf="${XDG_CONFIG_HOME:-${HOME}/.config}/rc"

for config in "@SYSCONFDIR@/rc.conf" "$_sysconf/rc.conf"; do
if [ -e "$config" ] && ! . "$config"; then
eerror "openrc-user: Failed loading $config"
exit 1
fi
done

case $1 in
start) _runlevel="${rc_default_runlevel:-default}";;
stop) _runlevel="shutdown";;
*) eerror "no argument given to $0" && exit 1
esac

mkdir -p "$_sysconf/runlevels/$_runlevel"

exec openrc --user "$_runlevel"
1 change: 1 addition & 0 deletions src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ subdir('openrc')
subdir('openrc-init')
subdir('openrc-run')
subdir('openrc-shutdown')
subdir('openrc-user')
subdir('poweroff')
subdir('rc-abort')
subdir('rc-depend')
Expand Down
7 changes: 7 additions & 0 deletions src/openrc-user/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
executable('openrc-user', ['openrc-user.c'],
c_args: [cc_pam_flags],
dependencies: pam_dep,
include_directories: [incdir, einfo_incdir, rc_incdir],
link_with: [libeinfo, librc],
install: true,
install_dir: rc_bindir)
210 changes: 210 additions & 0 deletions src/openrc-user/openrc-user.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
#include <einfo.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <pwd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <syslog.h>
#include <unistd.h>
#include <signal.h>

#ifdef HAVE_PAM
#include <security/pam_appl.h>
static struct pam_conv conv = { NULL, NULL };
static pam_handle_t *pamh = NULL;
#endif

#include "helpers.h"
#include "rc.h"

enum setup_mode {
CLIENT,
SERVER,
FAILED
};

static const struct passwd *user;
static char *fifopath;
static size_t logins;


static void cleanup(void) {
#ifdef HAVE_PAM
if (pamh) {
int rc;
if ((rc = pam_close_session(pamh, PAM_SILENT)) != PAM_SUCCESS)
elog(LOG_ERR, "Failed to close session: %s", pam_strerror(pamh, rc));
pam_end(pamh, rc);
}
#endif

if (exists(fifopath))
unlink(fifopath);
free(fifopath);
}

static enum setup_mode do_setup(const char *username) {
char *logname;
int nullfd;

xasprintf(&logname, "openrc-user[%s]", username);
setenv("EINFO_LOG", logname, true);
free(logname);

if (!(user = getpwnam(username))) {
elog(LOG_ERR, "getpwnam failed: %s", strerror(errno));
return false;
}

nullfd = open("/dev/null", O_RDWR);
dup2(nullfd, STDIN_FILENO);
close(nullfd);

xasprintf(&fifopath, "%s/users/%s", rc_svcdir(), user->pw_name);
if (mkfifo(fifopath, 0600) == -1 && errno != EEXIST) {
elog(LOG_ERR, "mkfifo failed: %s", strerror(errno));
return FAILED;
}

return errno == EEXIST ? CLIENT : SERVER;
}

static void do_openrc(bool start) {
pid_t child;
char *cmd;

switch ((child = fork())) {
case 0:
if (setgid(user->pw_gid) == -1 || setuid(user->pw_uid) == -1) {
elog(LOG_ERR, "Failed to drop permissions to user.");
exit(1);
}

setenv("HOME", user->pw_dir, true);
setenv("SHELL", user->pw_shell, true);

xasprintf(&cmd, "%s %s", RC_LIBEXECDIR "/sh/openrc-user.sh", start ? "start" : "stop");
execl(user->pw_shell, "-", "-c", cmd, NULL);

elog(LOG_ERR, "Failed to execl '%s - -c %s': %s.", user->pw_shell, cmd, strerror(errno));
exit(1);
case -1:
exit(1);
default:
break;
}

waitpid(child, NULL, 0);
}

static void open_session(void) {
#ifdef HAVE_PAM
bool pam_session = false;
int rc;

if ((rc = pam_start("openrc-user", user->pw_name, &conv, &pamh)) != PAM_SUCCESS)
elog(LOG_ERR, "Failed to start pam: %s", pam_strerror(pamh, rc));
else if ((rc = pam_open_session(pamh, PAM_SILENT)) != PAM_SUCCESS)
elog(LOG_ERR, "Failed to open session: %s", pam_strerror(pamh, rc));
else
pam_session = true;

for (char **env = pam_getenvlist(pamh); env && *env; env++) {
if (strchr(*env, '='))
putenv(xstrdup(*env));
else
unsetenv(*env);
}

if (!pam_session && pamh)
pam_end(pamh, rc);
#endif

return;
}

int main(int argc, char **argv) {
char *username, *cmd, *service;
FILE *fifo;

/* Avoid recursing pam stacks */
if ((service = getenv("PAM_SERVICE")) && strcmp(service, "openc-user"))
return 0;

switch (argc) {
case 3:
username = argv[1];
cmd = argv[2];
break;
case 2:
cmd = argv[1];
if ((username = getenv("PAM_USER")))
break;
/* falls through */
default:
fprintf(stderr, "%s: Not enough arguments.\n", argv[0]);
return 1;
}

switch (do_setup(username)) {
case FAILED:
return 1;
case CLIENT:
fifo = fopen(fifopath, "w");
fputs(cmd, fifo);
free(fifopath);
fclose(fifo);
return 0;
case SERVER:
setsid();
if (fork() != 0)
return 0;
break;
}

fifo = fopen(fifopath, "r");

atexit(cleanup);

/* we need to initgroups before pam is setup. */
if (initgroups(user->pw_name, user->pw_gid) == -1)
return 1;

open_session();

do_openrc(true);

for (;;) {
char buf[BUFSIZ];
size_t count;

if (!(fifo = fopen(fifopath, "r"))) {
if (errno != EINTR)
elog(LOG_ERR, "fopen failed: %s", strerror(errno));
continue;
}

count = fread(buf, BUFSIZ - 1, sizeof(char), fifo);
buf[count] = '\0';

fclose(fifo);

if (strcmp(buf, "start") == 0)
logins++;
else if (strcmp(buf, "stop") == 0)
logins--;
else if (strcmp(buf, "shutdown") == 0)
break;
else
elog(LOG_WARNING, "Ignoring unknown command %s.", buf);

if (logins == 0)
break;
}

do_openrc(false);

return 0;
}

0 comments on commit 7619c1e

Please sign in to comment.