chiark / gitweb /
util: introduce mkdir_p()
[elogind.git] / main.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 2 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <dbus/dbus.h>
23
24 #include <stdio.h>
25 #include <errno.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <getopt.h>
31
32 #include "manager.h"
33 #include "log.h"
34 #include "mount-setup.h"
35
36 static enum {
37         ACTION_RUN,
38         ACTION_HELP,
39         ACTION_TEST
40 } action = ACTION_RUN;
41
42 static char *default_unit = NULL;
43 static ManagerRunningAs running_as = _MANAGER_RUNNING_AS_INVALID;
44
45 static int set_default_unit(const char *u) {
46         char *c;
47
48         assert(u);
49
50         if (!(c = strdup(u)))
51                 return -ENOMEM;
52
53         free(default_unit);
54         default_unit = c;
55         return 0;
56 }
57
58 static int parse_proc_cmdline_word(const char *word) {
59
60         static const char * const rlmap[] = {
61                 "single", SPECIAL_RUNLEVEL1_TARGET,
62                 "-s",     SPECIAL_RUNLEVEL1_TARGET,
63                 "s",      SPECIAL_RUNLEVEL1_TARGET,
64                 "S",      SPECIAL_RUNLEVEL1_TARGET,
65                 "1",      SPECIAL_RUNLEVEL1_TARGET,
66                 "2",      SPECIAL_RUNLEVEL2_TARGET,
67                 "3",      SPECIAL_RUNLEVEL3_TARGET,
68                 "4",      SPECIAL_RUNLEVEL4_TARGET,
69                 "5",      SPECIAL_RUNLEVEL5_TARGET
70         };
71
72         if (startswith(word, "systemd.default="))
73                 return set_default_unit(word + 16);
74
75         else if (startswith(word, "systemd.log_target=")) {
76
77                 if (log_set_target_from_string(word + 19) < 0)
78                         log_warning("Failed to parse log target %s. Ignoring.", word + 19);
79
80         } else if (startswith(word, "systemd.log_level=")) {
81
82                 if (log_set_max_level_from_string(word + 18) < 0)
83                         log_warning("Failed to parse log level %s. Ignoring.", word + 18);
84
85         } else {
86                 unsigned i;
87
88                 /* SysV compatibility */
89
90                 for (i = 0; i < ELEMENTSOF(rlmap); i += 2)
91                         if (streq(word, rlmap[i]))
92                                 return set_default_unit(rlmap[i+1]);
93         }
94
95         return 0;
96 }
97
98 static int parse_proc_cmdline(void) {
99         char *line;
100         int r;
101         char *w;
102         size_t l;
103         char *state;
104
105         if ((r = read_one_line_file("/proc/cmdline", &line)) < 0) {
106                 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(errno));
107                 return 0;
108         }
109
110         FOREACH_WORD_QUOTED(w, l, line, state) {
111                 char *word;
112
113                 if (!(word = strndup(w, l))) {
114                         r = -ENOMEM;
115                         goto finish;
116                 }
117
118                 r = parse_proc_cmdline_word(word);
119                 free(word);
120
121                 if (r < 0)
122                         goto finish;
123         }
124
125         r = 0;
126
127 finish:
128         free(line);
129         return r;
130 }
131
132 static int parse_argv(int argc, char *argv[]) {
133
134         enum {
135                 ARG_LOG_LEVEL = 0x100,
136                 ARG_LOG_TARGET,
137                 ARG_DEFAULT,
138                 ARG_RUNNING_AS,
139                 ARG_TEST
140         };
141
142         static const struct option options[] = {
143                 { "log-level",  required_argument, NULL, ARG_LOG_LEVEL },
144                 { "log-target", required_argument, NULL, ARG_LOG_TARGET },
145                 { "default",    required_argument, NULL, ARG_DEFAULT },
146                 { "running-as", required_argument, NULL, ARG_RUNNING_AS },
147                 { "test",       no_argument,       NULL, ARG_TEST },
148                 { "help",       no_argument,       NULL, 'h' },
149                 { NULL,         0,                 NULL, 0 }
150         };
151
152         int c, r;
153
154         assert(argc >= 1);
155         assert(argv);
156
157         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
158
159                 switch (c) {
160
161                 case ARG_LOG_LEVEL:
162                         if ((r = log_set_max_level_from_string(optarg)) < 0) {
163                                 log_error("Failed to parse log level %s.", optarg);
164                                 return r;
165                         }
166
167                         break;
168
169                 case ARG_LOG_TARGET:
170
171                         if ((r = log_set_target_from_string(optarg)) < 0) {
172                                 log_error("Failed to parse log target %s.", optarg);
173                                 return r;
174                         }
175
176                         break;
177
178                 case ARG_DEFAULT:
179
180                         if ((r = set_default_unit(optarg)) < 0) {
181                                 log_error("Failed to set default unit %s: %s", optarg, strerror(-r));
182                                 return r;
183                         }
184
185                         break;
186
187                 case ARG_RUNNING_AS: {
188                         ManagerRunningAs as;
189
190                         if ((as = manager_running_as_from_string(optarg)) < 0) {
191                                 log_error("Failed to parse running as value %s", optarg);
192                                 return -EINVAL;
193                         }
194
195                         running_as = as;
196                         break;
197                 }
198
199                 case ARG_TEST:
200                         action = ACTION_TEST;
201                         break;
202
203                 case 'h':
204                         action = ACTION_HELP;
205                         break;
206
207                 case '?':
208                         return -EINVAL;
209
210                 default:
211                         log_error("Unknown option code %c", c);
212                         return -EINVAL;
213                 }
214
215         return 0;
216 }
217
218 static int help(void) {
219
220         printf("%s [options]\n\n"
221                "  -h --help               Show this help\n"
222                "     --default=UNIT       Set default unit\n"
223                "     --log-level=LEVEL    Set log level\n"
224                "     --log-target=TARGET  Set log target (console, syslog, kmsg)\n"
225                "     --running-as=AS      Set running as (init, system, session)\n"
226                "     --test               Determine startup sequence, dump it and exit\n",
227                __progname);
228
229         return 0;
230 }
231
232 int main(int argc, char *argv[]) {
233         Manager *m = NULL;
234         Unit *target = NULL;
235         Job *job = NULL;
236         int r, retval = 1;
237
238         if (getpid() == 1)
239                 running_as = MANAGER_INIT;
240         else if (getuid() == 0)
241                 running_as = MANAGER_SYSTEM;
242         else
243                 running_as = MANAGER_SESSION;
244
245         if (set_default_unit(SPECIAL_DEFAULT_TARGET) < 0)
246                 goto finish;
247
248         /* Mount /proc, /sys and friends, so that /proc/cmdline and
249          * /proc/$PID/fd is available. */
250         if (mount_setup() < 0)
251                 goto finish;
252
253         /* Reset all signal handlers. */
254         assert_se(reset_all_signal_handlers() == 0);
255
256         /* Close all open files */
257         assert_se(close_all_fds(NULL, 0) == 0);
258
259         if (running_as != MANAGER_SESSION)
260                 if (parse_proc_cmdline() < 0)
261                         goto finish;
262
263         log_parse_environment();
264
265         if (parse_argv(argc, argv) < 0)
266                 goto finish;
267
268         if (action == ACTION_HELP) {
269                 retval = help();
270                 goto finish;
271         }
272
273         assert_se(action == ACTION_RUN || action == ACTION_TEST);
274
275         /* Set up PATH unless it is already set */
276         setenv("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", false);
277
278         /* Move out of the way, so that we won't block unmounts */
279         assert_se(chdir("/")  == 0);
280
281         /* Become a session leader if we aren't one yet. */
282         setsid();
283
284         /* Disable the umask logic */
285         umask(0);
286
287         /* Make sure D-Bus doesn't fiddle with the SIGPIPE handlers */
288         dbus_connection_set_change_sigpipe(FALSE);
289
290         /* Open the logging devices, if possible and necessary*/
291         log_open_syslog();
292         log_open_kmsg();
293
294         log_debug("systemd running in %s mode.", manager_running_as_to_string(running_as));
295
296         if ((r = manager_new(running_as, &m)) < 0) {
297                 log_error("Failed to allocate manager object: %s", strerror(-r));
298                 goto finish;
299         }
300
301         if ((r = manager_coldplug(m)) < 0) {
302                 log_error("Failed to retrieve coldplug information: %s", strerror(-r));
303                 goto finish;
304         }
305
306         log_debug("Activating default unit: %s", default_unit);
307
308         if ((r = manager_load_unit(m, default_unit, &target)) < 0) {
309                 log_error("Failed to load default target: %s", strerror(-r));
310
311                 log_info("Trying to load rescue target...");
312                 if ((r = manager_load_unit(m, SPECIAL_RESCUE_TARGET, &target)) < 0) {
313                         log_error("Failed to load rescue target: %s", strerror(-r));
314                         goto finish;
315                 }
316         }
317
318         if (action == ACTION_TEST) {
319                 printf("→ By units:\n");
320                 manager_dump_units(m, stdout, "\t");
321         }
322
323         if ((r = manager_add_job(m, JOB_START, target, JOB_REPLACE, false, &job)) < 0) {
324                 log_error("Failed to start default target: %s", strerror(-r));
325                 goto finish;
326         }
327
328         if (action == ACTION_TEST) {
329                 printf("→ By jobs:\n");
330                 manager_dump_jobs(m, stdout, "\t");
331
332                 if (getpid() == 1)
333                         pause();
334
335                 retval = 0;
336                 goto finish;
337         }
338
339         if ((r = manager_loop(m)) < 0) {
340                 log_error("Failed to run mainloop: %s", strerror(-r));
341                 goto finish;
342         }
343
344         retval = 0;
345
346         log_debug("Exit.");
347
348 finish:
349         if (m)
350                 manager_free(m);
351
352         free(default_unit);
353
354         dbus_shutdown();
355
356         return retval;
357 }