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