summaryrefslogtreecommitdiffstats
path: root/daemon/main.c
diff options
context:
space:
mode:
authorGravatar Nikias Bassen2009-08-25 03:26:22 +0200
committerGravatar Nikias Bassen2009-08-25 03:26:22 +0200
commit4711a2b493f76561e9803bf7e8be77186f3e7798 (patch)
tree339684fe1b996e01047c3eb220a8e22e4491c5e2 /daemon/main.c
parentde30ca5d5c98a8cbee3d8748601519e2263b3e1d (diff)
downloadusbmuxd-4711a2b493f76561e9803bf7e8be77186f3e7798.tar.gz
usbmuxd-4711a2b493f76561e9803bf7e8be77186f3e7798.tar.bz2
Renamed directory 'usbmuxd' to more suitable 'daemon'.
Diffstat (limited to 'daemon/main.c')
-rw-r--r--daemon/main.c548
1 files changed, 548 insertions, 0 deletions
diff --git a/daemon/main.c b/daemon/main.c
new file mode 100644
index 0000000..dde99c2
--- /dev/null
+++ b/daemon/main.c
@@ -0,0 +1,548 @@
1/*
2 usbmuxd - iPhone/iPod Touch USB multiplex server daemon
3
4Copyright (C) 2009 Hector Martin "marcan" <hector@marcansoft.com>
5
6This program is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 2 or version 3.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19*/
20
21#define _BSD_SOURCE
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#include <stdio.h>
28#include <errno.h>
29#include <string.h>
30#include <stdlib.h>
31#include <signal.h>
32#include <unistd.h>
33#include <sys/socket.h>
34#include <sys/un.h>
35#include <sys/stat.h>
36#include <sys/types.h>
37#include <fcntl.h>
38#include <getopt.h>
39#include <pwd.h>
40#include <grp.h>
41
42#include "log.h"
43#include "usb.h"
44#include "device.h"
45#include "client.h"
46
47static const char *socket_path = "/var/run/usbmuxd";
48static const char *lockfile = "/var/run/usbmuxd.pid";
49
50int should_exit;
51
52static int verbose = 0;
53static int foreground = 0;
54static int drop_privileges = 0;
55static const char *drop_user = "usbmux";
56static int opt_udev = 0;
57static int opt_exit = 0;
58static int exit_signal = 0;
59static int daemon_pipe;
60
61static int report_to_parent = 0;
62
63int create_socket(void) {
64 struct sockaddr_un bind_addr;
65 int listenfd;
66
67 if(unlink(socket_path) == -1 && errno != ENOENT) {
68 usbmuxd_log(LL_FATAL, "unlink(%s) failed: %s", socket_path, strerror(errno));
69 return -1;
70 }
71
72 listenfd = socket(AF_UNIX, SOCK_STREAM, 0);
73 if (listenfd == -1) {
74 usbmuxd_log(LL_FATAL, "socket() failed: %s", strerror(errno));
75 return -1;
76 }
77
78 bzero(&bind_addr, sizeof(bind_addr));
79 bind_addr.sun_family = AF_UNIX;
80 strcpy(bind_addr.sun_path, socket_path);
81 if (bind(listenfd, (struct sockaddr*)&bind_addr, sizeof(bind_addr)) != 0) {
82 usbmuxd_log(LL_FATAL, "bind() failed: %s", strerror(errno));
83 return -1;
84 }
85
86 // Start listening
87 if (listen(listenfd, 5) != 0) {
88 usbmuxd_log(LL_FATAL, "listen() failed: %s", strerror(errno));
89 return -1;
90 }
91
92 chmod(socket_path, 0666);
93
94 return listenfd;
95}
96
97void handle_signal(int sig)
98{
99 if (sig != SIGUSR1) {
100 usbmuxd_log(LL_NOTICE,"Caught signal %d, exiting", sig);
101 should_exit = 1;
102 } else {
103 if(opt_udev) {
104 usbmuxd_log(LL_INFO, "Caught SIGUSR1, checking if we can terminate (no more devices attached)...");
105 if (device_get_count() > 0) {
106 // we can't quit, there are still devices attached.
107 usbmuxd_log(LL_NOTICE, "Refusing to terminate, there are still devices attached. Kill me with signal 15 (TERM) to force quit.");
108 } else {
109 // it's safe to quit
110 should_exit = 1;
111 }
112 } else {
113 usbmuxd_log(LL_INFO, "Caught SIGUSR1 but we weren't started in --udev mode, ignoring");
114 }
115 }
116}
117
118void set_signal_handlers(void)
119{
120 struct sigaction sa;
121 memset(&sa, 0, sizeof(struct sigaction));
122 sa.sa_handler = handle_signal;
123 sigaction(SIGINT, &sa, NULL);
124 sigaction(SIGQUIT, &sa, NULL);
125 sigaction(SIGTERM, &sa, NULL);
126 sigaction(SIGUSR1, &sa, NULL);
127}
128
129int main_loop(int listenfd)
130{
131 int to, cnt, i, dto;
132 struct fdlist pollfds;
133
134 while(!should_exit) {
135 usbmuxd_log(LL_FLOOD, "main_loop iteration");
136 to = usb_get_timeout();
137 usbmuxd_log(LL_FLOOD, "USB timeout is %d ms", to);
138 dto = device_get_timeout();
139 usbmuxd_log(LL_FLOOD, "Device timeout is %d ms", to);
140 if(dto < to)
141 to = dto;
142
143 fdlist_create(&pollfds);
144 fdlist_add(&pollfds, FD_LISTEN, listenfd, POLLIN);
145 usb_get_fds(&pollfds);
146 client_get_fds(&pollfds);
147 usbmuxd_log(LL_FLOOD, "fd count is %d", pollfds.count);
148
149 cnt = poll(pollfds.fds, pollfds.count, to);
150 usbmuxd_log(LL_FLOOD, "poll() returned %d", cnt);
151
152 if(cnt == -1) {
153 if(errno == EINTR && should_exit) {
154 usbmuxd_log(LL_INFO, "event processing interrupted");
155 fdlist_free(&pollfds);
156 return 0;
157 }
158 } else if(cnt == 0) {
159 if(usb_process() < 0) {
160 usbmuxd_log(LL_FATAL, "usb_process() failed");
161 fdlist_free(&pollfds);
162 return -1;
163 }
164 device_check_timeouts();
165 } else {
166 int done_usb = 0;
167 for(i=0; i<pollfds.count; i++) {
168 if(pollfds.fds[i].revents) {
169 if(!done_usb && pollfds.owners[i] == FD_USB) {
170 if(usb_process() < 0) {
171 usbmuxd_log(LL_FATAL, "usb_process() failed");
172 fdlist_free(&pollfds);
173 return -1;
174 }
175 done_usb = 1;
176 }
177 if(pollfds.owners[i] == FD_LISTEN) {
178 if(client_accept(listenfd) < 0) {
179 usbmuxd_log(LL_FATAL, "client_accept() failed");
180 fdlist_free(&pollfds);
181 return -1;
182 }
183 }
184 if(pollfds.owners[i] == FD_CLIENT) {
185 client_process(pollfds.fds[i].fd, pollfds.fds[i].revents);
186 }
187 }
188 }
189 }
190 fdlist_free(&pollfds);
191 }
192 return 0;
193}
194
195/**
196 * make this program run detached from the current console
197 */
198static int daemonize(void)
199{
200 pid_t pid;
201 pid_t sid;
202 int pfd[2];
203 int res;
204
205 // already a daemon
206 if (getppid() == 1)
207 return 0;
208
209 if((res = pipe(pfd)) < 0) {
210 usbmuxd_log(LL_FATAL, "pipe() failed.");
211 return res;
212 }
213
214 pid = fork();
215 if (pid < 0) {
216 usbmuxd_log(LL_FATAL, "fork() failed.");
217 return pid;
218 }
219
220 if (pid > 0) {
221 // exit parent process
222 int status;
223 close(pfd[1]);
224
225 if((res = read(pfd[0],&status,sizeof(int))) != sizeof(int)) {
226 fprintf(stderr, "usbmuxd: ERROR: Failed to get init status from child, check syslog for messages.\n");
227 exit(1);
228 }
229 if(status != 0)
230 fprintf(stderr, "usbmuxd: ERROR: Child process exited with error %d, check syslog for messages.\n", status);
231 exit(status);
232 }
233 // At this point we are executing as the child process
234 // but we need to do one more fork
235
236 daemon_pipe = pfd[1];
237 close(pfd[0]);
238 report_to_parent = 1;
239
240 // Change the file mode mask
241 umask(0);
242
243 // Create a new SID for the child process
244 sid = setsid();
245 if (sid < 0) {
246 usbmuxd_log(LL_FATAL, "setsid() failed.");
247 return -1;
248 }
249
250 pid = fork();
251 if (pid < 0) {
252 usbmuxd_log(LL_FATAL, "fork() failed (second).");
253 return pid;
254 }
255
256 if (pid > 0) {
257 // exit parent process
258 close(daemon_pipe);
259 exit(0);
260 }
261
262 // Change the current working directory.
263 if ((chdir("/")) < 0) {
264 usbmuxd_log(LL_FATAL, "chdir() failed");
265 return -2;
266 }
267 // Redirect standard files to /dev/null
268 if (!freopen("/dev/null", "r", stdin)) {
269 usbmuxd_log(LL_FATAL, "Redirection of stdin failed.");
270 return -3;
271 }
272 if (!freopen("/dev/null", "w", stdout)) {
273 usbmuxd_log(LL_FATAL, "Redirection of stdout failed.");
274 return -3;
275 }
276
277 return 0;
278}
279
280static int notify_parent(int status)
281{
282 int res;
283
284 report_to_parent = 0;
285 if ((res = write(daemon_pipe, &status, sizeof(int))) != sizeof(int)) {
286 usbmuxd_log(LL_FATAL, "Could not notify parent!");
287 if(res >= 0)
288 return -2;
289 else
290 return res;
291 }
292 close(daemon_pipe);
293 if (!freopen("/dev/null", "w", stderr)) {
294 usbmuxd_log(LL_FATAL, "Redirection of stderr failed.");
295 return -1;
296 }
297 return 0;
298}
299
300static void usage()
301{
302 printf("usage: usbmuxd [options]\n");
303 printf("\t-h|--help Print this message.\n");
304 printf("\t-v|--verbose Be verbose (use twice or more to increase).\n");
305 printf("\t-f|--foreground Do not daemonize (implies one -v).\n");
306 printf("\t-U|--user[=USER] Change to this user after startup (needs usb privileges).\n");
307 printf("\t If USER is not specified, defaults to usbmux.\n");
308 printf("\t-u|--udev Run in udev operation mode.\n");
309 printf("\t-x|--exit Tell a running instance to exit if there are no devices\n");
310 printf("\t connected (must be in udev mode).\n");
311 printf("\t-X|--force-exit Tell a running instance to exit, even if there are still\n");
312 printf("\t devices connected (always works).\n");
313 printf("\n");
314}
315
316static void parse_opts(int argc, char **argv)
317{
318 static struct option longopts[] = {
319 {"help", 0, NULL, 'h'},
320 {"foreground", 0, NULL, 'f'},
321 {"verbose", 0, NULL, 'v'},
322 {"user", 2, NULL, 'U'},
323 {"udev", 0, NULL, 'u'},
324 {"exit", 0, NULL, 'x'},
325 {"force-exit", 0, NULL, 'X'},
326 {NULL, 0, NULL, 0}
327 };
328 int c;
329
330 while (1) {
331 c = getopt_long(argc, argv, "hfvuU::xX", longopts, (int *) 0);
332 if (c == -1) {
333 break;
334 }
335
336 switch (c) {
337 case 'h':
338 usage();
339 exit(0);
340 case 'f':
341 foreground = 1;
342 break;
343 case 'v':
344 ++verbose;
345 break;
346 case 'U':
347 drop_privileges = 1;
348 if(optarg)
349 drop_user = optarg;
350 break;
351 case 'u':
352 opt_udev = 1;
353 break;
354 case 'x':
355 opt_exit = 1;
356 exit_signal = SIGUSR1;
357 break;
358 case 'X':
359 opt_exit = 1;
360 exit_signal = SIGTERM;
361 break;
362 default:
363 usage();
364 exit(2);
365 }
366 }
367}
368
369int main(int argc, char *argv[])
370{
371 int listenfd;
372 int res = 0;
373 int lfd;
374 struct flock lock;
375 char pids[10];
376
377 parse_opts(argc, argv);
378
379 argc -= optind;
380 argv += optind;
381
382 if (!foreground) {
383 verbose += LL_WARNING;
384 log_enable_syslog();
385 } else {
386 verbose += LL_NOTICE;
387 }
388
389 /* set log level to specified verbosity */
390 log_level = verbose;
391
392 usbmuxd_log(LL_NOTICE, "usbmux v0.1 starting up");
393 should_exit = 0;
394
395 set_signal_handlers();
396
397 res = lfd = open(lockfile, O_WRONLY|O_CREAT, 0644);
398 if(res == -1) {
399 usbmuxd_log(LL_FATAL, "Could not open lockfile");
400 goto terminate;
401 }
402 lock.l_type = F_WRLCK;
403 lock.l_whence = SEEK_SET;
404 lock.l_start = 0;
405 lock.l_len = 0;
406 fcntl(lfd, F_GETLK, &lock);
407 close(lfd);
408 if (lock.l_type != F_UNLCK) {
409 if (opt_exit) {
410 if (lock.l_pid && !kill(lock.l_pid, 0)) {
411 usbmuxd_log(LL_NOTICE, "Sending signal %d to instance with pid %d", exit_signal, lock.l_pid);
412 res = 0;
413 if (kill(lock.l_pid, exit_signal) < 0) {
414 usbmuxd_log(LL_FATAL, "Could not deliver signal %d to pid %d", exit_signal, lock.l_pid);
415 res = -1;
416 }
417 goto terminate;
418 } else {
419 usbmuxd_log(LL_ERROR, "Could not determine pid of the other running instance!");
420 res = -1;
421 goto terminate;
422 }
423 } else {
424 if (!opt_udev) {
425 usbmuxd_log(LL_ERROR, "Another instance is already running (pid %d). exiting.", lock.l_pid);
426 res = -1;
427 } else {
428 usbmuxd_log(LL_NOTICE, "Another instance is already running (pid %d). exiting.", lock.l_pid);
429 res = 0;
430 }
431 goto terminate;
432 }
433 }
434 unlink(lockfile);
435
436 if (opt_exit) {
437 usbmuxd_log(LL_NOTICE, "No running instance found, none killed. exiting.");
438 goto terminate;
439 }
440
441 if (!foreground) {
442 if ((res = daemonize()) < 0) {
443 fprintf(stderr, "usbmuxd: FATAL: Could not daemonize!\n");
444 usbmuxd_log(LL_FATAL, "Could not daemonize!");
445 goto terminate;
446 }
447 }
448
449 // now open the lockfile and place the lock
450 res = lfd = open(lockfile, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644);
451 if(res < 0) {
452 usbmuxd_log(LL_FATAL, "Could not open lockfile");
453 goto terminate;
454 }
455 lock.l_type = F_WRLCK;
456 lock.l_whence = SEEK_SET;
457 lock.l_start = 0;
458 lock.l_len = 0;
459 if ((res = fcntl(lfd, F_SETLK, &lock)) < 0) {
460 usbmuxd_log(LL_FATAL, "Lockfile locking failed!");
461 goto terminate;
462 }
463 sprintf(pids, "%d", getpid());
464 if ((res = write(lfd, pids, strlen(pids))) != strlen(pids)) {
465 usbmuxd_log(LL_FATAL, "Could not write pidfile!");
466 if(res >= 0)
467 res = -2;
468 goto terminate;
469 }
470
471 usbmuxd_log(LL_INFO, "Creating socket");
472 res = listenfd = create_socket();
473 if(listenfd < 0)
474 goto terminate;
475
476 // drop elevated privileges
477 if (drop_privileges && (getuid() == 0 || geteuid() == 0)) {
478 struct passwd *pw = getpwnam(drop_user);
479 if (!pw) {
480 usbmuxd_log(LL_FATAL, "Dropping privileges failed, check if user '%s' exists!", drop_user);
481 res = -1;
482 goto terminate;
483 }
484
485 if ((res = initgroups(drop_user, pw->pw_gid)) < 0) {
486 usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set supplementary groups)");
487 goto terminate;
488 }
489 if ((res = setgid(pw->pw_gid)) < 0) {
490 usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set group ID to %d)", pw->pw_gid);
491 goto terminate;
492 }
493 if ((res = setuid(pw->pw_uid)) < 0) {
494 usbmuxd_log(LL_FATAL, "Failed to drop privileges (cannot set user ID to %d)", pw->pw_uid);
495 goto terminate;
496 }
497
498 // security check
499 if (setuid(0) != -1) {
500 usbmuxd_log(LL_FATAL, "Failed to drop privileges properly!");
501 res = -1;
502 goto terminate;
503 }
504 if (getuid() != pw->pw_uid || getgid() != pw->pw_gid) {
505 usbmuxd_log(LL_FATAL, "Failed to drop privileges properly!");
506 res = -1;
507 goto terminate;
508 }
509 usbmuxd_log(LL_NOTICE, "Successfully dropped privileges to '%s'", drop_user);
510 }
511
512 client_init();
513 device_init();
514 usbmuxd_log(LL_INFO, "Initializing USB");
515 if((res = usb_init()) < 0)
516 goto terminate;
517
518 usbmuxd_log(LL_INFO, "%d device%s detected", res, (res==1)?"":"s");
519
520 usbmuxd_log(LL_NOTICE, "Initialization complete");
521
522 if (report_to_parent)
523 if((res = notify_parent(0)) < 0)
524 goto terminate;
525
526 res = main_loop(listenfd);
527 if(res < 0)
528 usbmuxd_log(LL_FATAL, "main_loop failed");
529
530 usbmuxd_log(LL_NOTICE, "usbmux shutting down");
531 device_kill_connections();
532 usb_shutdown();
533 device_shutdown();
534 client_shutdown();
535 usbmuxd_log(LL_NOTICE, "Shutdown complete");
536
537terminate:
538 log_disable_syslog();
539
540 if (res < 0)
541 res = -res;
542 else
543 res = 0;
544 if (report_to_parent)
545 notify_parent(res);
546
547 return res;
548}