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