chiark / gitweb /
nspawn: split long message into two lines
[elogind.git] / src / getty-generator / getty-generator.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
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 Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 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   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <string.h>
23 #include <errno.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26
27 #include "log.h"
28 #include "util.h"
29 #include "mkdir.h"
30 #include "unit-name.h"
31 #include "virt.h"
32 #include "fileio.h"
33 #include "path-util.h"
34
35 static const char *arg_dest = "/tmp";
36
37 static int add_symlink(const char *fservice, const char *tservice) {
38         _cleanup_free_ char *from = NULL, *to = NULL;
39         int r;
40
41         assert(fservice);
42         assert(tservice);
43
44         from = strappend(SYSTEM_DATA_UNIT_PATH "/", fservice);
45         if (!from)
46                 return log_oom();
47
48         to = strjoin(arg_dest,"/getty.target.wants/", tservice, NULL);
49         if (!to)
50                 return log_oom();
51
52         mkdir_parents_label(to, 0755);
53
54         r = symlink(from, to);
55         if (r < 0) {
56                 if (errno == EEXIST)
57                         /* In case console=hvc0 is passed this will very likely result in EEXIST */
58                         return 0;
59                 else {
60                         log_error("Failed to create symlink %s: %m", to);
61                         return -errno;
62                 }
63         }
64
65         return 0;
66 }
67
68 static int add_serial_getty(const char *tty) {
69         _cleanup_free_ char *n = NULL;
70
71         assert(tty);
72
73         log_debug("Automatically adding serial getty for /dev/%s.", tty);
74
75         n = unit_name_replace_instance("serial-getty@.service", tty);
76         if (!n)
77                 return log_oom();
78
79         return add_symlink("serial-getty@.service", n);
80 }
81
82 static int add_container_getty(const char *tty) {
83         _cleanup_free_ char *n = NULL;
84
85         assert(tty);
86
87         log_debug("Automatically adding container getty for /dev/pts/%s.", tty);
88
89         n = unit_name_replace_instance("container-getty@.service", tty);
90         if (!n)
91                 return log_oom();
92
93         return add_symlink("container-getty@.service", n);
94 }
95
96 static int verify_tty(const char *name) {
97         _cleanup_close_ int fd = -1;
98         const char *p;
99
100         /* Some TTYs are weird and have been enumerated but don't work
101          * when you try to use them, such as classic ttyS0 and
102          * friends. Let's check that and open the device and run
103          * isatty() on it. */
104
105         p = strappenda("/dev/", name);
106
107         /* O_NONBLOCK is essential here, to make sure we don't wait
108          * for DCD */
109         fd = open(p, O_RDWR|O_NONBLOCK|O_NOCTTY|O_CLOEXEC|O_NOFOLLOW);
110         if (fd < 0)
111                 return -errno;
112
113         errno = 0;
114         if (isatty(fd) <= 0)
115                 return errno ? -errno : -EIO;
116
117         return 0;
118 }
119
120 int main(int argc, char *argv[]) {
121
122         static const char virtualization_consoles[] =
123                 "hvc0\0"
124                 "xvc0\0"
125                 "hvsi0\0"
126                 "sclp_line0\0"
127                 "ttysclp0\0"
128                 "3270!tty1\0";
129
130         _cleanup_free_ char *active = NULL;
131         const char *j;
132         int r;
133
134         if (argc > 1 && argc != 4) {
135                 log_error("This program takes three or no arguments.");
136                 return EXIT_FAILURE;
137         }
138
139         if (argc > 1)
140                 arg_dest = argv[1];
141
142         log_set_target(LOG_TARGET_SAFE);
143         log_parse_environment();
144         log_open();
145
146         umask(0022);
147
148         if (detect_container(NULL) > 0) {
149                 _cleanup_free_ char *container_ttys = NULL;
150
151                 log_debug("Automatically adding console shell.");
152
153                 if (add_symlink("console-getty.service", "console-getty.service") < 0)
154                         return EXIT_FAILURE;
155
156                 /* When $container_ttys is set for PID 1, spawn
157                  * gettys on all ptys named therein. Note that despite
158                  * the variable name we only support ptys here. */
159
160                 r = getenv_for_pid(1, "container_ttys", &container_ttys);
161                 if (r > 0) {
162                         char *w, *state;
163                         size_t l;
164
165                         FOREACH_WORD(w, l, container_ttys, state) {
166                                 const char *t;
167                                 char tty[l + 1];
168
169                                 memcpy(tty, w, l);
170                                 tty[l] = 0;
171
172                                 /* First strip off /dev/ if it is specified */
173                                 t = path_startswith(tty, "/dev/");
174                                 if (!t)
175                                         t = tty;
176
177                                 /* Then, make sure it's actually a pty */
178                                 t = path_startswith(t, "pts/");
179                                 if (!t)
180                                         continue;
181
182                                 if (add_container_getty(t) < 0)
183                                         return EXIT_FAILURE;
184                         }
185                 }
186
187                 /* Don't add any further magic if we are in a container */
188                 return EXIT_SUCCESS;
189         }
190
191         if (read_one_line_file("/sys/class/tty/console/active", &active) >= 0) {
192                 char *w, *state;
193                 size_t l;
194
195                 /* Automatically add in a serial getty on all active
196                  * kernel consoles */
197                 FOREACH_WORD(w, l, active, state) {
198                         _cleanup_free_ char *tty = NULL;
199
200                         tty = strndup(w, l);
201                         if (!tty) {
202                                 log_oom();
203                                 return EXIT_FAILURE;
204                         }
205
206                         if (isempty(tty) || tty_is_vc(tty))
207                                 continue;
208
209                         if (verify_tty(tty) < 0)
210                                 continue;
211
212                         /* We assume that gettys on virtual terminals are
213                          * started via manual configuration and do this magic
214                          * only for non-VC terminals. */
215
216                         if (add_serial_getty(tty) < 0)
217                                 return EXIT_FAILURE;
218                 }
219         }
220
221         /* Automatically add in a serial getty on the first
222          * virtualizer console */
223         NULSTR_FOREACH(j, virtualization_consoles) {
224                 _cleanup_free_ char *p = NULL;
225
226                 p = strappend("/sys/class/tty/", j);
227                 if (!p) {
228                         log_oom();
229                         return EXIT_FAILURE;
230                 }
231
232                 if (access(p, F_OK) < 0)
233                         continue;
234
235                 if (add_serial_getty(j) < 0)
236                         return EXIT_FAILURE;
237         }
238
239         return EXIT_SUCCESS;
240 }