chiark / gitweb /
socket.c: make use of union sockaddr_union
[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
129         _cleanup_free_ char *active = NULL;
130         const char *j;
131         int r;
132
133         if (argc > 1 && argc != 4) {
134                 log_error("This program takes three or no arguments.");
135                 return EXIT_FAILURE;
136         }
137
138         if (argc > 1)
139                 arg_dest = argv[1];
140
141         log_set_target(LOG_TARGET_SAFE);
142         log_parse_environment();
143         log_open();
144
145         umask(0022);
146
147         if (detect_container(NULL) > 0) {
148                 _cleanup_free_ char *container_ttys = NULL;
149
150                 log_debug("Automatically adding console shell.");
151
152                 if (add_symlink("console-getty.service", "console-getty.service") < 0)
153                         return EXIT_FAILURE;
154
155                 /* When $container_ttys is set for PID 1, spawn
156                  * gettys on all ptys named therein. Note that despite
157                  * the variable name we only support ptys here. */
158
159                 r = getenv_for_pid(1, "container_ttys", &container_ttys);
160                 if (r > 0) {
161                         char *w, *state;
162                         size_t l;
163
164                         FOREACH_WORD(w, l, container_ttys, state) {
165                                 const char *t;
166                                 char tty[l + 1];
167
168                                 memcpy(tty, w, l);
169                                 tty[l] = 0;
170
171                                 /* First strip off /dev/ if it is specified */
172                                 t = path_startswith(tty, "/dev/");
173                                 if (!t)
174                                         t = tty;
175
176                                 /* Then, make sure it's actually a pty */
177                                 t = path_startswith(t, "pts/");
178                                 if (!t)
179                                         continue;
180
181                                 if (add_container_getty(t) < 0)
182                                         return EXIT_FAILURE;
183                         }
184                 }
185
186                 /* Don't add any further magic if we are in a container */
187                 return EXIT_SUCCESS;
188         }
189
190         if (read_one_line_file("/sys/class/tty/console/active", &active) >= 0) {
191                 char *w, *state;
192                 size_t l;
193
194                 /* Automatically add in a serial getty on all active
195                  * kernel consoles */
196                 FOREACH_WORD(w, l, active, state) {
197                         _cleanup_free_ char *tty = NULL;
198
199                         tty = strndup(w, l);
200                         if (!tty) {
201                                 log_oom();
202                                 return EXIT_FAILURE;
203                         }
204
205                         if (isempty(tty) || tty_is_vc(tty))
206                                 continue;
207
208                         if (verify_tty(tty) < 0)
209                                 continue;
210
211                         /* We assume that gettys on virtual terminals are
212                          * started via manual configuration and do this magic
213                          * only for non-VC terminals. */
214
215                         if (add_serial_getty(tty) < 0)
216                                 return EXIT_FAILURE;
217                 }
218         }
219
220         /* Automatically add in a serial getty on the first
221          * virtualizer console */
222         NULSTR_FOREACH(j, virtualization_consoles) {
223                 _cleanup_free_ char *p = NULL;
224
225                 p = strappend("/sys/class/tty/", j);
226                 if (!p) {
227                         log_oom();
228                         return EXIT_FAILURE;
229                 }
230
231                 if (access(p, F_OK) < 0)
232                         continue;
233
234                 if (add_serial_getty(j) < 0)
235                         return EXIT_FAILURE;
236         }
237
238         return EXIT_SUCCESS;
239 }