fix back the controlloop inversion.
[jack.git] / jackd / jackstart.c
blob0bd21da8d2f3d9b91fcedf90784ea2ad85f813c6
1 /* Copyright (C) 2002 Fernando Lopez-Lezcano
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2 of the License, or
6 (at your option) any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 Jackstart is based on code and concepts found in sucap.c, written by
18 Finn Arne Gangstad <finnag@guardian.no> and givertcap.c, written by
19 Tommi Ilmonen, Tommi.Ilmonen@hut.fi
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <errno.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <pwd.h>
29 #include <grp.h>
30 #include <unistd.h>
31 #include <sys/wait.h>
32 #include <errno.h>
33 #include <string.h>
35 #include <config.h>
37 #undef _POSIX_SOURCE
38 #include <sys/capability.h>
40 #include "jack/start.h"
41 #include "md5.h"
42 #include "jack_md5.h"
44 #define READ_BLOCKSIZE 4096
46 /* JACK_LOCATION must be passed on the gcc command line */
47 static char *jackd_bin_path = JACK_LOCATION "/jackd";
49 static char *jackd_md5_sum = JACKD_MD5_SUM;
52 static int check_capabilities (void)
54 cap_t caps = cap_init();
55 cap_flag_value_t cap;
56 pid_t pid;
57 int have_all_caps = 1;
59 if (caps == NULL) {
60 fprintf (stderr, "jackstart: could not allocate capability working storage\n");
61 return 0;
63 pid = getpid ();
64 cap_clear (caps);
65 if (capgetp (pid, caps)) {
66 fprintf (stderr, "jackstart: could not get capabilities for process %d\n", pid);
67 return 0;
69 /* check that we are able to give capabilites to other processes */
70 cap_get_flag(caps, CAP_SETPCAP, CAP_EFFECTIVE, &cap);
71 if (cap == CAP_CLEAR) {
72 have_all_caps = 0;
73 goto done;
75 /* check that we have the capabilities we want to transfer */
76 cap_get_flag(caps, CAP_SYS_NICE, CAP_EFFECTIVE, &cap);
77 if (cap == CAP_CLEAR) {
78 have_all_caps = 0;
79 goto done;
81 cap_get_flag(caps, CAP_SYS_RESOURCE, CAP_EFFECTIVE, &cap);
82 if (cap == CAP_CLEAR) {
83 have_all_caps = 0;
84 goto done;
86 cap_get_flag(caps, CAP_IPC_LOCK, CAP_EFFECTIVE, &cap);
87 if (cap == CAP_CLEAR) {
88 have_all_caps = 0;
89 goto done;
91 done:
92 cap_free (caps);
93 return have_all_caps;
97 static int give_capabilities (pid_t pid)
99 cap_t caps = cap_init();
100 const unsigned caps_size = 4;
101 cap_value_t cap_list[] =
102 { CAP_SETPCAP, CAP_SYS_NICE, CAP_SYS_RESOURCE, CAP_IPC_LOCK} ;
104 if (caps == NULL) {
105 fprintf (stderr, "jackstart: could not allocate capability working storage\n");
106 return -1;
108 cap_clear(caps);
109 if (capgetp (pid, caps)) {
110 fprintf (stderr, "jackstart: could not get capabilities for process %d\n", pid);
111 cap_clear(caps);
113 cap_set_flag(caps, CAP_EFFECTIVE, caps_size, cap_list , CAP_SET);
114 cap_set_flag(caps, CAP_INHERITABLE, caps_size, cap_list , CAP_SET);
115 cap_set_flag(caps, CAP_PERMITTED, caps_size, cap_list , CAP_SET);
116 if (capsetp (pid, caps)) {
117 fprintf (stderr, "jackstart: could not give capabilities: %s\n", strerror (errno));
118 cap_free (caps);
119 return -1;
121 cap_free (caps);
122 return 0;
125 static int check_binary (const char *binpath)
127 struct stat status;
128 FILE *binstream;
130 if (lstat(jackd_bin_path, &status)) {
131 fprintf (stderr, "jackstart: could not stat %s: %s\n",
132 binpath, strerror(errno));
133 return -1;
135 if (!(S_ISREG(status.st_mode))) {
136 fprintf (stderr, "jackstart: %s is not a regular file\n",
137 binpath);
138 return -1;
140 if (status.st_uid != 0) {
141 fprintf (stderr, "jackstart: %s is not owned by root\n",
142 binpath);
143 return -1;
145 if ((status.st_mode & 022) != 0) {
146 fprintf (stderr,
147 "jackstart: %s mode %o writeable by non-root users\n",
148 binpath, status.st_mode & 07777);
149 return -1;
151 if ((binstream = fopen (binpath, "r")) == NULL) {
152 fprintf (stderr, "jackstart: can't open %s for reading: %s\n",
153 binpath, strerror(errno));
154 return -1;
155 } else {
156 /* md5sum the executable file, check man evp for more details */
157 size_t sum;
158 md5_t ctx;
159 char buffer[READ_BLOCKSIZE + 72];
160 unsigned char md_value[MD5_SIZE];
161 char md_string[3];
162 int i, j;
164 md5_init(&ctx);
165 while (1) {
166 size_t n;
167 sum = 0;
168 do {
169 n = fread (buffer + sum, 1, READ_BLOCKSIZE - sum, binstream);
170 sum += n;
171 } while (sum < READ_BLOCKSIZE && n != 0);
172 if (n == 0 && ferror (binstream)) {
173 fprintf (stderr, "jackstart: error while reading %s: %s\n", binpath, strerror(errno));
174 return -1;
176 if (n == 0) {
177 break;
179 md5_process(&ctx, buffer, READ_BLOCKSIZE);
181 if (sum > 0)
182 md5_process(&ctx, buffer, sum);
183 if (fclose (binstream)) {
184 fprintf (stderr, "jackstart: could not close %s after reading: %s\n", binpath, strerror(errno));
186 md5_finish(&ctx, md_value);
187 for(i = 0, j = 0; i < sizeof(md_value); i++, j+=2) {
188 sprintf(md_string, "%02x", md_value[i]);
189 if (md_string[0] != jackd_md5_sum[j] ||
190 md_string[1] != jackd_md5_sum[j+1]) {
191 fprintf (stderr, "jackstart: md5 checksum for %s does not match\n", binpath);
192 return -1;
196 return 0;
199 int main(int argc, char **argv)
201 uid_t uid, euid;
202 pid_t pid, parent_pid;
203 gid_t gid;
204 int pipe_fds[2];
205 int err;
207 parent_pid = getpid ();
209 /* get real user and group ids, effective user id */
210 uid = getuid ();
211 gid = getgid ();
212 euid = geteuid ();
214 /* are we running suid root? */
215 if (uid != 0) {
216 if (euid != 0) {
217 fprintf (stderr, "jackstart: not running suid root, can't use capabilities\n");
218 fprintf (stderr, " (currently running with uid=%d and euid=%d),\n", uid, euid);
219 fprintf (stderr, " make jackstart suid root or start jackd directly\n\n");
222 /* see if we can get the required capabilities */
223 if (check_capabilities () == 0) {
224 size_t size;
225 cap_t cap = cap_init();
226 capgetp(0, cap);
227 fprintf (stderr, "jackstart: cannot get realtime capabilities, current capabilities are:\n");
228 fprintf (stderr, " %s\n", cap_to_text(cap, &size));
229 fprintf (stderr, " probably running under a kernel with capabilities disabled,\n");
230 fprintf (stderr, " a suitable kernel would have printed something like \"=eip\"\n\n");
233 /* check the executable, owner, permissions, md5 checksum */
234 if (check_binary(jackd_bin_path)) {
235 exit(1);
238 /* set process group to current pid */
239 if (setpgid (0, getpid())) {
240 fprintf (stderr, "jackstart: failed to set process group: %s\n",
241 strerror(errno));
242 exit (1);
245 /* create pipe to synchronize with jackd */
246 if (pipe (pipe_fds)) {
247 fprintf (stderr, "jackstart: could not create pipe: %s\n",
248 strerror(errno));
249 exit (1);
252 /* make sure the file descriptors are the right ones,
253 otherwise dup them, this is to make sure that both
254 jackstart and jackd use the same fds
256 if (pipe_fds[0] != PIPE_READ_FD) {
257 if (dup2 (pipe_fds[0], PIPE_READ_FD) != PIPE_READ_FD) {
258 fprintf (stderr, "jackstart: could not dup pipe read file descriptor: %s\n",
259 strerror(errno));
260 exit (1);
263 if (pipe_fds[1] != PIPE_WRITE_FD) {
264 if (dup2(pipe_fds[1], PIPE_WRITE_FD)!=PIPE_WRITE_FD) {
265 fprintf (stderr, "jackstart: could not dup pipe write file descriptor: %s\n",
266 strerror(errno));
267 exit (1);
270 /* fork off a child to wait for jackd to start */
271 fflush(NULL);
272 pid = fork();
273 if (pid == -1) {
274 fprintf (stderr, "jackstart: fork failed\n");
275 exit (1);
277 if (pid) {
278 /* mother process: drops privileges, execs jackd */
279 close(PIPE_READ_FD);
281 /* get rid of any supplemental groups */
282 if (!getuid () && setgroups (0, 0)) {
283 fprintf (stderr, "jackstart: setgroups failed: %s\n", strerror(errno));
284 exit (1);
287 /* set gid and uid */
288 setregid(gid, gid);
289 setreuid(uid, uid);
290 execvp(jackd_bin_path, argv);
292 /* we could not start jackd, clean up and exit */
293 fprintf(stderr, "jackstart: unable to execute %s: %s\n", jackd_bin_path, strerror(errno));
294 close (PIPE_WRITE_FD);
295 wait (&err);
296 exit (1);
297 } else {
298 /* child process: grants privileges to jackd */
299 close(PIPE_WRITE_FD);
301 /* wait for jackd to start */
302 while (1) {
303 int ret;
304 char c;
306 /* picking up pipe closure is a tricky business.
307 this seems to work as well as anything else.
310 ret = read(PIPE_READ_FD, &c, 1);
311 fprintf (stderr, "back from read, ret = %d errno == %s\n", ret, strerror (errno));
312 if (ret == 1) {
313 break;
314 } else if (errno != EINTR) {
315 break;
319 /* set privileges on jackd process */
320 give_capabilities (parent_pid);
322 exit (0);