Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : :
3 : : #include <errno.h>
4 : : #include <fcntl.h>
5 : : #include <string.h>
6 : : #include <sys/stat.h>
7 : : #include <unistd.h>
8 : :
9 : : #include "sd-messages.h"
10 : :
11 : : #include "alloc-util.h"
12 : : #include "errno-util.h"
13 : : #include "fd-util.h"
14 : : #include "fileio.h"
15 : : #include "format-util.h"
16 : : #include "logind-acl.h"
17 : : #include "logind-seat-dbus.h"
18 : : #include "logind-seat.h"
19 : : #include "logind-session-dbus.h"
20 : : #include "mkdir.h"
21 : : #include "parse-util.h"
22 : : #include "path-util.h"
23 : : #include "stdio-util.h"
24 : : #include "string-util.h"
25 : : #include "terminal-util.h"
26 : : #include "tmpfile-util.h"
27 : : #include "util.h"
28 : :
29 : 0 : int seat_new(Seat** ret, Manager *m, const char *id) {
30 : 0 : _cleanup_(seat_freep) Seat *s = NULL;
31 : : int r;
32 : :
33 [ # # ]: 0 : assert(ret);
34 [ # # ]: 0 : assert(m);
35 [ # # ]: 0 : assert(id);
36 : :
37 [ # # ]: 0 : if (!seat_name_is_valid(id))
38 : 0 : return -EINVAL;
39 : :
40 : 0 : s = new(Seat, 1);
41 [ # # ]: 0 : if (!s)
42 : 0 : return -ENOMEM;
43 : :
44 : 0 : *s = (Seat) {
45 : : .manager = m,
46 : : };
47 : :
48 : 0 : s->state_file = path_join("/run/systemd/seats", id);
49 [ # # ]: 0 : if (!s->state_file)
50 : 0 : return -ENOMEM;
51 : :
52 : 0 : s->id = basename(s->state_file);
53 : :
54 : 0 : r = hashmap_put(m->seats, s->id, s);
55 [ # # ]: 0 : if (r < 0)
56 : 0 : return r;
57 : :
58 : 0 : *ret = TAKE_PTR(s);
59 : 0 : return 0;
60 : : }
61 : :
62 : 0 : Seat* seat_free(Seat *s) {
63 [ # # ]: 0 : if (!s)
64 : 0 : return NULL;
65 : :
66 [ # # ]: 0 : if (s->in_gc_queue)
67 [ # # # # : 0 : LIST_REMOVE(gc_queue, s->manager->seat_gc_queue, s);
# # # # ]
68 : :
69 [ # # ]: 0 : while (s->sessions)
70 : 0 : session_free(s->sessions);
71 : :
72 [ # # ]: 0 : assert(!s->active);
73 : :
74 [ # # ]: 0 : while (s->devices)
75 : 0 : device_free(s->devices);
76 : :
77 : 0 : hashmap_remove(s->manager->seats, s->id);
78 : :
79 : 0 : free(s->positions);
80 : 0 : free(s->state_file);
81 : :
82 : 0 : return mfree(s);
83 : : }
84 : :
85 : 0 : int seat_save(Seat *s) {
86 : 0 : _cleanup_free_ char *temp_path = NULL;
87 : 0 : _cleanup_fclose_ FILE *f = NULL;
88 : : int r;
89 : :
90 [ # # ]: 0 : assert(s);
91 : :
92 [ # # ]: 0 : if (!s->started)
93 : 0 : return 0;
94 : :
95 : 0 : r = mkdir_safe_label("/run/systemd/seats", 0755, 0, 0, MKDIR_WARN_MODE);
96 [ # # ]: 0 : if (r < 0)
97 : 0 : goto fail;
98 : :
99 : 0 : r = fopen_temporary(s->state_file, &f, &temp_path);
100 [ # # ]: 0 : if (r < 0)
101 : 0 : goto fail;
102 : :
103 : 0 : (void) fchmod(fileno(f), 0644);
104 : :
105 : 0 : fprintf(f,
106 : : "# This is private data. Do not parse.\n"
107 : : "IS_SEAT0=%i\n"
108 : : "CAN_MULTI_SESSION=%i\n"
109 : : "CAN_TTY=%i\n"
110 : : "CAN_GRAPHICAL=%i\n",
111 : 0 : seat_is_seat0(s),
112 : 0 : seat_can_multi_session(s),
113 : 0 : seat_can_tty(s),
114 : 0 : seat_can_graphical(s));
115 : :
116 [ # # ]: 0 : if (s->active) {
117 [ # # ]: 0 : assert(s->active->user);
118 : :
119 : 0 : fprintf(f,
120 : : "ACTIVE=%s\n"
121 : : "ACTIVE_UID="UID_FMT"\n",
122 : 0 : s->active->id,
123 : 0 : s->active->user->uid);
124 : : }
125 : :
126 [ # # ]: 0 : if (s->sessions) {
127 : : Session *i;
128 : :
129 : 0 : fputs("SESSIONS=", f);
130 [ # # ]: 0 : LIST_FOREACH(sessions_by_seat, i, s->sessions) {
131 : 0 : fprintf(f,
132 : : "%s%c",
133 : : i->id,
134 [ # # ]: 0 : i->sessions_by_seat_next ? ' ' : '\n');
135 : : }
136 : :
137 : 0 : fputs("UIDS=", f);
138 [ # # ]: 0 : LIST_FOREACH(sessions_by_seat, i, s->sessions)
139 : 0 : fprintf(f,
140 : : UID_FMT"%c",
141 : 0 : i->user->uid,
142 [ # # ]: 0 : i->sessions_by_seat_next ? ' ' : '\n');
143 : : }
144 : :
145 : 0 : r = fflush_and_check(f);
146 [ # # ]: 0 : if (r < 0)
147 : 0 : goto fail;
148 : :
149 [ # # ]: 0 : if (rename(temp_path, s->state_file) < 0) {
150 : 0 : r = -errno;
151 : 0 : goto fail;
152 : : }
153 : :
154 : 0 : return 0;
155 : :
156 : 0 : fail:
157 : 0 : (void) unlink(s->state_file);
158 : :
159 [ # # ]: 0 : if (temp_path)
160 : 0 : (void) unlink(temp_path);
161 : :
162 [ # # ]: 0 : return log_error_errno(r, "Failed to save seat data %s: %m", s->state_file);
163 : : }
164 : :
165 : 0 : int seat_load(Seat *s) {
166 [ # # ]: 0 : assert(s);
167 : :
168 : : /* There isn't actually anything to read here ... */
169 : :
170 : 0 : return 0;
171 : : }
172 : :
173 : 0 : static int vt_allocate(unsigned vtnr) {
174 : : char p[sizeof("/dev/tty") + DECIMAL_STR_MAX(unsigned)];
175 : 0 : _cleanup_close_ int fd = -1;
176 : :
177 [ # # ]: 0 : assert(vtnr >= 1);
178 : :
179 [ # # ]: 0 : xsprintf(p, "/dev/tty%u", vtnr);
180 : 0 : fd = open_terminal(p, O_RDWR|O_NOCTTY|O_CLOEXEC);
181 [ # # ]: 0 : if (fd < 0)
182 : 0 : return fd;
183 : :
184 : 0 : return 0;
185 : : }
186 : :
187 : 0 : int seat_preallocate_vts(Seat *s) {
188 : 0 : int r = 0;
189 : : unsigned i;
190 : :
191 [ # # ]: 0 : assert(s);
192 [ # # ]: 0 : assert(s->manager);
193 : :
194 [ # # ]: 0 : if (s->manager->n_autovts <= 0)
195 : 0 : return 0;
196 : :
197 [ # # ]: 0 : if (!seat_has_vts(s))
198 : 0 : return 0;
199 : :
200 [ # # ]: 0 : log_debug("Preallocating VTs...");
201 : :
202 [ # # ]: 0 : for (i = 1; i <= s->manager->n_autovts; i++) {
203 : : int q;
204 : :
205 : 0 : q = vt_allocate(i);
206 [ # # ]: 0 : if (q < 0)
207 [ # # ]: 0 : r = log_error_errno(q, "Failed to preallocate VT %u: %m", i);
208 : : }
209 : :
210 : 0 : return r;
211 : : }
212 : :
213 : 0 : int seat_apply_acls(Seat *s, Session *old_active) {
214 : : int r;
215 : :
216 [ # # ]: 0 : assert(s);
217 : :
218 : 0 : r = devnode_acl_all(s->id,
219 : : false,
220 : 0 : !!old_active, old_active ? old_active->user->uid : 0,
221 [ # # # # ]: 0 : !!s->active, s->active ? s->active->user->uid : 0);
222 : :
223 [ # # ]: 0 : if (r < 0)
224 [ # # ]: 0 : return log_error_errno(r, "Failed to apply ACLs: %m");
225 : :
226 : 0 : return 0;
227 : : }
228 : :
229 : 0 : int seat_set_active(Seat *s, Session *session) {
230 : : Session *old_active;
231 : :
232 [ # # ]: 0 : assert(s);
233 [ # # # # ]: 0 : assert(!session || session->seat == s);
234 : :
235 [ # # ]: 0 : if (session == s->active)
236 : 0 : return 0;
237 : :
238 : 0 : old_active = s->active;
239 : 0 : s->active = session;
240 : :
241 [ # # ]: 0 : if (old_active) {
242 : 0 : session_device_pause_all(old_active);
243 : 0 : session_send_changed(old_active, "Active", NULL);
244 : : }
245 : :
246 : 0 : (void) seat_apply_acls(s, old_active);
247 : :
248 [ # # # # ]: 0 : if (session && session->started) {
249 : 0 : session_send_changed(session, "Active", NULL);
250 : 0 : session_device_resume_all(session);
251 : : }
252 : :
253 [ # # # # ]: 0 : if (!session || session->started)
254 : 0 : seat_send_changed(s, "ActiveSession", NULL);
255 : :
256 : 0 : seat_save(s);
257 : :
258 [ # # ]: 0 : if (session) {
259 : 0 : session_save(session);
260 : 0 : user_save(session->user);
261 : : }
262 : :
263 [ # # ]: 0 : if (old_active) {
264 : 0 : session_save(old_active);
265 [ # # # # ]: 0 : if (!session || session->user != old_active->user)
266 : 0 : user_save(old_active->user);
267 : : }
268 : :
269 : 0 : return 0;
270 : : }
271 : :
272 : 0 : int seat_switch_to(Seat *s, unsigned num) {
273 : : /* Public session positions skip 0 (there is only F1-F12). Maybe it
274 : : * will get reassigned in the future, so return error for now. */
275 [ # # ]: 0 : if (num == 0)
276 : 0 : return -EINVAL;
277 : :
278 [ # # # # ]: 0 : if (num >= s->position_count || !s->positions[num]) {
279 : : /* allow switching to unused VTs to trigger auto-activate */
280 [ # # # # ]: 0 : if (seat_has_vts(s) && num < 64)
281 : 0 : return chvt(num);
282 : :
283 : 0 : return -EINVAL;
284 : : }
285 : :
286 : 0 : return session_activate(s->positions[num]);
287 : : }
288 : :
289 : 0 : int seat_switch_to_next(Seat *s) {
290 : : unsigned start, i;
291 : :
292 [ # # ]: 0 : if (s->position_count == 0)
293 : 0 : return -EINVAL;
294 : :
295 : 0 : start = 1;
296 [ # # # # ]: 0 : if (s->active && s->active->position > 0)
297 : 0 : start = s->active->position;
298 : :
299 [ # # ]: 0 : for (i = start + 1; i < s->position_count; ++i)
300 [ # # ]: 0 : if (s->positions[i])
301 : 0 : return session_activate(s->positions[i]);
302 : :
303 [ # # ]: 0 : for (i = 1; i < start; ++i)
304 [ # # ]: 0 : if (s->positions[i])
305 : 0 : return session_activate(s->positions[i]);
306 : :
307 : 0 : return -EINVAL;
308 : : }
309 : :
310 : 0 : int seat_switch_to_previous(Seat *s) {
311 : : unsigned start, i;
312 : :
313 [ # # ]: 0 : if (s->position_count == 0)
314 : 0 : return -EINVAL;
315 : :
316 : 0 : start = 1;
317 [ # # # # ]: 0 : if (s->active && s->active->position > 0)
318 : 0 : start = s->active->position;
319 : :
320 [ # # ]: 0 : for (i = start - 1; i > 0; --i)
321 [ # # ]: 0 : if (s->positions[i])
322 : 0 : return session_activate(s->positions[i]);
323 : :
324 [ # # ]: 0 : for (i = s->position_count - 1; i > start; --i)
325 [ # # ]: 0 : if (s->positions[i])
326 : 0 : return session_activate(s->positions[i]);
327 : :
328 : 0 : return -EINVAL;
329 : : }
330 : :
331 : 0 : int seat_active_vt_changed(Seat *s, unsigned vtnr) {
332 : 0 : Session *i, *new_active = NULL;
333 : : int r;
334 : :
335 [ # # ]: 0 : assert(s);
336 [ # # ]: 0 : assert(vtnr >= 1);
337 : :
338 [ # # ]: 0 : if (!seat_has_vts(s))
339 : 0 : return -EINVAL;
340 : :
341 [ # # ]: 0 : log_debug("VT changed to %u", vtnr);
342 : :
343 : : /* we might have earlier closing sessions on the same VT, so try to
344 : : * find a running one first */
345 [ # # ]: 0 : LIST_FOREACH(sessions_by_seat, i, s->sessions)
346 [ # # # # ]: 0 : if (i->vtnr == vtnr && !i->stopping) {
347 : 0 : new_active = i;
348 : 0 : break;
349 : : }
350 : :
351 [ # # ]: 0 : if (!new_active) {
352 : : /* no running one? then we can't decide which one is the
353 : : * active one, let the first one win */
354 [ # # ]: 0 : LIST_FOREACH(sessions_by_seat, i, s->sessions)
355 [ # # ]: 0 : if (i->vtnr == vtnr) {
356 : 0 : new_active = i;
357 : 0 : break;
358 : : }
359 : : }
360 : :
361 : 0 : r = seat_set_active(s, new_active);
362 : 0 : manager_spawn_autovt(s->manager, vtnr);
363 : :
364 : 0 : return r;
365 : : }
366 : :
367 : 0 : int seat_read_active_vt(Seat *s) {
368 : : char t[64];
369 : : ssize_t k;
370 : : int vtnr;
371 : :
372 [ # # ]: 0 : assert(s);
373 : :
374 [ # # ]: 0 : if (!seat_has_vts(s))
375 : 0 : return 0;
376 : :
377 [ # # ]: 0 : if (lseek(s->manager->console_active_fd, SEEK_SET, 0) < 0)
378 [ # # ]: 0 : return log_error_errno(errno, "lseek on console_active_fd failed: %m");
379 : :
380 : 0 : k = read(s->manager->console_active_fd, t, sizeof(t)-1);
381 [ # # ]: 0 : if (k <= 0) {
382 [ # # # # ]: 0 : log_error("Failed to read current console: %s", k < 0 ? strerror_safe(errno) : "EOF");
383 [ # # ]: 0 : return k < 0 ? -errno : -EIO;
384 : : }
385 : :
386 : 0 : t[k] = 0;
387 : 0 : truncate_nl(t);
388 : :
389 : 0 : vtnr = vtnr_from_tty(t);
390 [ # # ]: 0 : if (vtnr < 0) {
391 [ # # ]: 0 : log_error_errno(vtnr, "Hm, /sys/class/tty/tty0/active is badly formatted: %m");
392 : 0 : return -EIO;
393 : : }
394 : :
395 : 0 : return seat_active_vt_changed(s, vtnr);
396 : : }
397 : :
398 : 0 : int seat_start(Seat *s) {
399 [ # # ]: 0 : assert(s);
400 : :
401 [ # # ]: 0 : if (s->started)
402 : 0 : return 0;
403 : :
404 : 0 : log_struct(LOG_INFO,
405 : : "MESSAGE_ID=" SD_MESSAGE_SEAT_START_STR,
406 : : "SEAT_ID=%s", s->id,
407 : : LOG_MESSAGE("New seat %s.", s->id));
408 : :
409 : : /* Initialize VT magic stuff */
410 : 0 : seat_preallocate_vts(s);
411 : :
412 : : /* Read current VT */
413 : 0 : seat_read_active_vt(s);
414 : :
415 : 0 : s->started = true;
416 : :
417 : : /* Save seat data */
418 : 0 : seat_save(s);
419 : :
420 : 0 : seat_send_signal(s, true);
421 : :
422 : 0 : return 0;
423 : : }
424 : :
425 : 0 : int seat_stop(Seat *s, bool force) {
426 : : int r;
427 : :
428 [ # # ]: 0 : assert(s);
429 : :
430 [ # # ]: 0 : if (s->started)
431 : 0 : log_struct(LOG_INFO,
432 : : "MESSAGE_ID=" SD_MESSAGE_SEAT_STOP_STR,
433 : : "SEAT_ID=%s", s->id,
434 : : LOG_MESSAGE("Removed seat %s.", s->id));
435 : :
436 : 0 : r = seat_stop_sessions(s, force);
437 : :
438 : 0 : (void) unlink(s->state_file);
439 : 0 : seat_add_to_gc_queue(s);
440 : :
441 [ # # ]: 0 : if (s->started)
442 : 0 : seat_send_signal(s, false);
443 : :
444 : 0 : s->started = false;
445 : :
446 : 0 : return r;
447 : : }
448 : :
449 : 0 : int seat_stop_sessions(Seat *s, bool force) {
450 : : Session *session;
451 : 0 : int r = 0, k;
452 : :
453 [ # # ]: 0 : assert(s);
454 : :
455 [ # # ]: 0 : LIST_FOREACH(sessions_by_seat, session, s->sessions) {
456 : 0 : k = session_stop(session, force);
457 [ # # ]: 0 : if (k < 0)
458 : 0 : r = k;
459 : : }
460 : :
461 : 0 : return r;
462 : : }
463 : :
464 : 0 : void seat_evict_position(Seat *s, Session *session) {
465 : : Session *iter;
466 : 0 : unsigned pos = session->position;
467 : :
468 : 0 : session->position = 0;
469 : :
470 [ # # ]: 0 : if (pos == 0)
471 : 0 : return;
472 : :
473 [ # # # # ]: 0 : if (pos < s->position_count && s->positions[pos] == session) {
474 : 0 : s->positions[pos] = NULL;
475 : :
476 : : /* There might be another session claiming the same
477 : : * position (eg., during gdm->session transition), so let's look
478 : : * for it and set it on the free slot. */
479 [ # # ]: 0 : LIST_FOREACH(sessions_by_seat, iter, s->sessions) {
480 [ # # # # ]: 0 : if (iter->position == pos && session_get_state(iter) != SESSION_CLOSING) {
481 : 0 : s->positions[pos] = iter;
482 : 0 : break;
483 : : }
484 : : }
485 : : }
486 : : }
487 : :
488 : 0 : void seat_claim_position(Seat *s, Session *session, unsigned pos) {
489 : : /* with VTs, the position is always the same as the VTnr */
490 [ # # ]: 0 : if (seat_has_vts(s))
491 : 0 : pos = session->vtnr;
492 : :
493 [ # # ]: 0 : if (!GREEDY_REALLOC0(s->positions, s->position_count, pos + 1))
494 : 0 : return;
495 : :
496 : 0 : seat_evict_position(s, session);
497 : :
498 : 0 : session->position = pos;
499 [ # # ]: 0 : if (pos > 0)
500 : 0 : s->positions[pos] = session;
501 : : }
502 : :
503 : 0 : static void seat_assign_position(Seat *s, Session *session) {
504 : : unsigned pos;
505 : :
506 [ # # ]: 0 : if (session->position > 0)
507 : 0 : return;
508 : :
509 [ # # ]: 0 : for (pos = 1; pos < s->position_count; ++pos)
510 [ # # ]: 0 : if (!s->positions[pos])
511 : 0 : break;
512 : :
513 : 0 : seat_claim_position(s, session, pos);
514 : : }
515 : :
516 : 0 : int seat_attach_session(Seat *s, Session *session) {
517 [ # # ]: 0 : assert(s);
518 [ # # ]: 0 : assert(session);
519 [ # # ]: 0 : assert(!session->seat);
520 : :
521 [ # # ]: 0 : if (!seat_has_vts(s) != !session->vtnr)
522 : 0 : return -EINVAL;
523 : :
524 : 0 : session->seat = s;
525 [ # # # # ]: 0 : LIST_PREPEND(sessions_by_seat, s->sessions, session);
526 : 0 : seat_assign_position(s, session);
527 : :
528 : : /* On seats with VTs, the VT logic defines which session is active. On
529 : : * seats without VTs, we automatically activate new sessions. */
530 [ # # ]: 0 : if (!seat_has_vts(s))
531 : 0 : seat_set_active(s, session);
532 : :
533 : 0 : return 0;
534 : : }
535 : :
536 : 0 : void seat_complete_switch(Seat *s) {
537 : : Session *session;
538 : :
539 [ # # ]: 0 : assert(s);
540 : :
541 : : /* if no session-switch is pending or if it got canceled, do nothing */
542 [ # # ]: 0 : if (!s->pending_switch)
543 : 0 : return;
544 : :
545 : 0 : session = TAKE_PTR(s->pending_switch);
546 : :
547 : 0 : seat_set_active(s, session);
548 : : }
549 : :
550 : 0 : bool seat_has_vts(Seat *s) {
551 [ # # ]: 0 : assert(s);
552 : :
553 [ # # # # ]: 0 : return seat_is_seat0(s) && s->manager->console_active_fd >= 0;
554 : : }
555 : :
556 : 0 : bool seat_is_seat0(Seat *s) {
557 [ # # ]: 0 : assert(s);
558 : :
559 : 0 : return s->manager->seat0 == s;
560 : : }
561 : :
562 : 0 : bool seat_can_multi_session(Seat *s) {
563 [ # # ]: 0 : assert(s);
564 : :
565 : 0 : return seat_has_vts(s);
566 : : }
567 : :
568 : 0 : bool seat_can_tty(Seat *s) {
569 [ # # ]: 0 : assert(s);
570 : :
571 : 0 : return seat_has_vts(s);
572 : : }
573 : :
574 : 0 : bool seat_has_master_device(Seat *s) {
575 [ # # ]: 0 : assert(s);
576 : :
577 : : /* device list is ordered by "master" flag */
578 [ # # # # ]: 0 : return !!s->devices && s->devices->master;
579 : : }
580 : :
581 : 0 : bool seat_can_graphical(Seat *s) {
582 [ # # ]: 0 : assert(s);
583 : :
584 : 0 : return seat_has_master_device(s);
585 : : }
586 : :
587 : 0 : int seat_get_idle_hint(Seat *s, dual_timestamp *t) {
588 : : Session *session;
589 : 0 : bool idle_hint = true;
590 : 0 : dual_timestamp ts = DUAL_TIMESTAMP_NULL;
591 : :
592 [ # # ]: 0 : assert(s);
593 : :
594 [ # # ]: 0 : LIST_FOREACH(sessions_by_seat, session, s->sessions) {
595 : : dual_timestamp k;
596 : : int ih;
597 : :
598 : 0 : ih = session_get_idle_hint(session, &k);
599 [ # # ]: 0 : if (ih < 0)
600 : 0 : return ih;
601 : :
602 [ # # ]: 0 : if (!ih) {
603 [ # # ]: 0 : if (!idle_hint) {
604 [ # # ]: 0 : if (k.monotonic > ts.monotonic)
605 : 0 : ts = k;
606 : : } else {
607 : 0 : idle_hint = false;
608 : 0 : ts = k;
609 : : }
610 [ # # ]: 0 : } else if (idle_hint) {
611 : :
612 [ # # ]: 0 : if (k.monotonic > ts.monotonic)
613 : 0 : ts = k;
614 : : }
615 : : }
616 : :
617 [ # # ]: 0 : if (t)
618 : 0 : *t = ts;
619 : :
620 : 0 : return idle_hint;
621 : : }
622 : :
623 : 0 : bool seat_may_gc(Seat *s, bool drop_not_started) {
624 [ # # ]: 0 : assert(s);
625 : :
626 [ # # # # ]: 0 : if (drop_not_started && !s->started)
627 : 0 : return true;
628 : :
629 [ # # ]: 0 : if (seat_is_seat0(s))
630 : 0 : return false;
631 : :
632 : 0 : return !seat_has_master_device(s);
633 : : }
634 : :
635 : 0 : void seat_add_to_gc_queue(Seat *s) {
636 [ # # ]: 0 : assert(s);
637 : :
638 [ # # ]: 0 : if (s->in_gc_queue)
639 : 0 : return;
640 : :
641 [ # # # # ]: 0 : LIST_PREPEND(gc_queue, s->manager->seat_gc_queue, s);
642 : 0 : s->in_gc_queue = true;
643 : : }
644 : :
645 : 0 : static bool seat_name_valid_char(char c) {
646 : : return
647 [ # # # # ]: 0 : (c >= 'a' && c <= 'z') ||
648 [ # # # # ]: 0 : (c >= 'A' && c <= 'Z') ||
649 [ # # # # ]: 0 : (c >= '0' && c <= '9') ||
650 [ # # # # ]: 0 : IN_SET(c, '-', '_');
651 : : }
652 : :
653 : 0 : bool seat_name_is_valid(const char *name) {
654 : : const char *p;
655 : :
656 [ # # ]: 0 : assert(name);
657 : :
658 [ # # ]: 0 : if (!startswith(name, "seat"))
659 : 0 : return false;
660 : :
661 [ # # ]: 0 : if (!name[4])
662 : 0 : return false;
663 : :
664 [ # # ]: 0 : for (p = name; *p; p++)
665 [ # # ]: 0 : if (!seat_name_valid_char(*p))
666 : 0 : return false;
667 : :
668 [ # # ]: 0 : if (strlen(name) > 255)
669 : 0 : return false;
670 : :
671 : 0 : return true;
672 : : }
|