Skip to content

Commit 7619c1e

Browse files
committed
autostart
1 parent 7a608db commit 7619c1e

File tree

5 files changed

+271
-0
lines changed

5 files changed

+271
-0
lines changed

init.d/users.in

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#!@SBINDIR@/openrc-run
2+
# Copyright (c) 2017 The OpenRC Authors.
3+
# See the Authors file at the top-level directory of this distribution and
4+
# https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS
5+
#
6+
# This file is part of OpenRC. It is subject to the license terms in
7+
# the LICENSE file found in the top-level directory of this
8+
# distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE
9+
# This file may not be copied, modified, propagated, or distributed
10+
# except according to the terms contained in the LICENSE file.
11+
12+
description="Starts and stops openrc --user for system users."
13+
14+
start() {
15+
mkdir "$RC_SVCDIR"/users || return 1
16+
for user in ${rc_users}; do
17+
ebegin "Starting user session for $user"
18+
openrc-user $user start
19+
eend $?
20+
done
21+
return 0
22+
}
23+
24+
stop() {
25+
for user in "$RC_SVCDIR"/users/*; do
26+
ebegin "Stopping user session for $user"
27+
openrc-user ${user##*/} shutdown
28+
eend $?
29+
done
30+
return 0
31+
}

sh/openrc-user.sh.in

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!@SHELL@
2+
3+
. @LIBEXECDIR@/sh/functions.sh
4+
5+
_sysconf="${XDG_CONFIG_HOME:-${HOME}/.config}/rc"
6+
7+
for config in "@SYSCONFDIR@/rc.conf" "$_sysconf/rc.conf"; do
8+
if [ -e "$config" ] && ! . "$config"; then
9+
eerror "openrc-user: Failed loading $config"
10+
exit 1
11+
fi
12+
done
13+
14+
case $1 in
15+
start) _runlevel="${rc_default_runlevel:-default}";;
16+
stop) _runlevel="shutdown";;
17+
*) eerror "no argument given to $0" && exit 1
18+
esac
19+
20+
mkdir -p "$_sysconf/runlevels/$_runlevel"
21+
22+
exec openrc --user "$_runlevel"

src/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ subdir('openrc')
1818
subdir('openrc-init')
1919
subdir('openrc-run')
2020
subdir('openrc-shutdown')
21+
subdir('openrc-user')
2122
subdir('poweroff')
2223
subdir('rc-abort')
2324
subdir('rc-depend')

src/openrc-user/meson.build

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
executable('openrc-user', ['openrc-user.c'],
2+
c_args: [cc_pam_flags],
3+
dependencies: pam_dep,
4+
include_directories: [incdir, einfo_incdir, rc_incdir],
5+
link_with: [libeinfo, librc],
6+
install: true,
7+
install_dir: rc_bindir)

src/openrc-user/openrc-user.c

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
#include <einfo.h>
2+
#include <errno.h>
3+
#include <fcntl.h>
4+
#include <grp.h>
5+
#include <pwd.h>
6+
#include <stdlib.h>
7+
#include <sys/types.h>
8+
#include <sys/wait.h>
9+
#include <syslog.h>
10+
#include <unistd.h>
11+
#include <signal.h>
12+
13+
#ifdef HAVE_PAM
14+
#include <security/pam_appl.h>
15+
static struct pam_conv conv = { NULL, NULL };
16+
static pam_handle_t *pamh = NULL;
17+
#endif
18+
19+
#include "helpers.h"
20+
#include "rc.h"
21+
22+
enum setup_mode {
23+
CLIENT,
24+
SERVER,
25+
FAILED
26+
};
27+
28+
static const struct passwd *user;
29+
static char *fifopath;
30+
static size_t logins;
31+
32+
33+
static void cleanup(void) {
34+
#ifdef HAVE_PAM
35+
if (pamh) {
36+
int rc;
37+
if ((rc = pam_close_session(pamh, PAM_SILENT)) != PAM_SUCCESS)
38+
elog(LOG_ERR, "Failed to close session: %s", pam_strerror(pamh, rc));
39+
pam_end(pamh, rc);
40+
}
41+
#endif
42+
43+
if (exists(fifopath))
44+
unlink(fifopath);
45+
free(fifopath);
46+
}
47+
48+
static enum setup_mode do_setup(const char *username) {
49+
char *logname;
50+
int nullfd;
51+
52+
xasprintf(&logname, "openrc-user[%s]", username);
53+
setenv("EINFO_LOG", logname, true);
54+
free(logname);
55+
56+
if (!(user = getpwnam(username))) {
57+
elog(LOG_ERR, "getpwnam failed: %s", strerror(errno));
58+
return false;
59+
}
60+
61+
nullfd = open("/dev/null", O_RDWR);
62+
dup2(nullfd, STDIN_FILENO);
63+
close(nullfd);
64+
65+
xasprintf(&fifopath, "%s/users/%s", rc_svcdir(), user->pw_name);
66+
if (mkfifo(fifopath, 0600) == -1 && errno != EEXIST) {
67+
elog(LOG_ERR, "mkfifo failed: %s", strerror(errno));
68+
return FAILED;
69+
}
70+
71+
return errno == EEXIST ? CLIENT : SERVER;
72+
}
73+
74+
static void do_openrc(bool start) {
75+
pid_t child;
76+
char *cmd;
77+
78+
switch ((child = fork())) {
79+
case 0:
80+
if (setgid(user->pw_gid) == -1 || setuid(user->pw_uid) == -1) {
81+
elog(LOG_ERR, "Failed to drop permissions to user.");
82+
exit(1);
83+
}
84+
85+
setenv("HOME", user->pw_dir, true);
86+
setenv("SHELL", user->pw_shell, true);
87+
88+
xasprintf(&cmd, "%s %s", RC_LIBEXECDIR "/sh/openrc-user.sh", start ? "start" : "stop");
89+
execl(user->pw_shell, "-", "-c", cmd, NULL);
90+
91+
elog(LOG_ERR, "Failed to execl '%s - -c %s': %s.", user->pw_shell, cmd, strerror(errno));
92+
exit(1);
93+
case -1:
94+
exit(1);
95+
default:
96+
break;
97+
}
98+
99+
waitpid(child, NULL, 0);
100+
}
101+
102+
static void open_session(void) {
103+
#ifdef HAVE_PAM
104+
bool pam_session = false;
105+
int rc;
106+
107+
if ((rc = pam_start("openrc-user", user->pw_name, &conv, &pamh)) != PAM_SUCCESS)
108+
elog(LOG_ERR, "Failed to start pam: %s", pam_strerror(pamh, rc));
109+
else if ((rc = pam_open_session(pamh, PAM_SILENT)) != PAM_SUCCESS)
110+
elog(LOG_ERR, "Failed to open session: %s", pam_strerror(pamh, rc));
111+
else
112+
pam_session = true;
113+
114+
for (char **env = pam_getenvlist(pamh); env && *env; env++) {
115+
if (strchr(*env, '='))
116+
putenv(xstrdup(*env));
117+
else
118+
unsetenv(*env);
119+
}
120+
121+
if (!pam_session && pamh)
122+
pam_end(pamh, rc);
123+
#endif
124+
125+
return;
126+
}
127+
128+
int main(int argc, char **argv) {
129+
char *username, *cmd, *service;
130+
FILE *fifo;
131+
132+
/* Avoid recursing pam stacks */
133+
if ((service = getenv("PAM_SERVICE")) && strcmp(service, "openc-user"))
134+
return 0;
135+
136+
switch (argc) {
137+
case 3:
138+
username = argv[1];
139+
cmd = argv[2];
140+
break;
141+
case 2:
142+
cmd = argv[1];
143+
if ((username = getenv("PAM_USER")))
144+
break;
145+
/* falls through */
146+
default:
147+
fprintf(stderr, "%s: Not enough arguments.\n", argv[0]);
148+
return 1;
149+
}
150+
151+
switch (do_setup(username)) {
152+
case FAILED:
153+
return 1;
154+
case CLIENT:
155+
fifo = fopen(fifopath, "w");
156+
fputs(cmd, fifo);
157+
free(fifopath);
158+
fclose(fifo);
159+
return 0;
160+
case SERVER:
161+
setsid();
162+
if (fork() != 0)
163+
return 0;
164+
break;
165+
}
166+
167+
fifo = fopen(fifopath, "r");
168+
169+
atexit(cleanup);
170+
171+
/* we need to initgroups before pam is setup. */
172+
if (initgroups(user->pw_name, user->pw_gid) == -1)
173+
return 1;
174+
175+
open_session();
176+
177+
do_openrc(true);
178+
179+
for (;;) {
180+
char buf[BUFSIZ];
181+
size_t count;
182+
183+
if (!(fifo = fopen(fifopath, "r"))) {
184+
if (errno != EINTR)
185+
elog(LOG_ERR, "fopen failed: %s", strerror(errno));
186+
continue;
187+
}
188+
189+
count = fread(buf, BUFSIZ - 1, sizeof(char), fifo);
190+
buf[count] = '\0';
191+
192+
fclose(fifo);
193+
194+
if (strcmp(buf, "start") == 0)
195+
logins++;
196+
else if (strcmp(buf, "stop") == 0)
197+
logins--;
198+
else if (strcmp(buf, "shutdown") == 0)
199+
break;
200+
else
201+
elog(LOG_WARNING, "Ignoring unknown command %s.", buf);
202+
203+
if (logins == 0)
204+
break;
205+
}
206+
207+
do_openrc(false);
208+
209+
return 0;
210+
}

0 commit comments

Comments
 (0)