LCOV - code coverage report
Current view: top level - shared - pty.c (source / functions) Hit Total Coverage
Test: systemd test coverage Lines: 207 273 75.8 %
Date: 2015-07-29 18:47:03 Functions: 24 27 88.9 %

          Line data    Source code
       1             : /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
       2             : 
       3             : /***
       4             :   This file is part of systemd.
       5             : 
       6             :   Copyright 2014 David Herrmann <dh.herrmann@gmail.com>
       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             : /*
      23             :  * PTY
      24             :  * A PTY object represents a single PTY connection between a master and a
      25             :  * child. The child process is fork()ed so the caller controls what program
      26             :  * will be run.
      27             :  *
      28             :  * Programs like /bin/login tend to perform a vhangup() on their TTY
      29             :  * before running the login procedure. This also causes the pty master
      30             :  * to get a EPOLLHUP event as long as no client has the TTY opened.
      31             :  * This means, we cannot use the TTY connection as reliable way to track
      32             :  * the client. Instead, we _must_ rely on the PID of the client to track
      33             :  * them.
      34             :  * However, this has the side effect that if the client forks and the
      35             :  * parent exits, we loose them and restart the client. But this seems to
      36             :  * be the expected behavior so we implement it here.
      37             :  *
      38             :  * Unfortunately, epoll always polls for EPOLLHUP so as long as the
      39             :  * vhangup() is ongoing, we will _always_ get EPOLLHUP and cannot sleep.
      40             :  * This gets worse if the client closes the TTY but doesn't exit.
      41             :  * Therefore, the fd must be edge-triggered in the epoll-set so we
      42             :  * only get the events once they change.
      43             :  */
      44             : 
      45             : #include <errno.h>
      46             : #include <fcntl.h>
      47             : #include <signal.h>
      48             : #include <stdbool.h>
      49             : #include <stdint.h>
      50             : #include <stdlib.h>
      51             : #include <sys/epoll.h>
      52             : #include <sys/ioctl.h>
      53             : #include <sys/uio.h>
      54             : #include <sys/wait.h>
      55             : #include <termios.h>
      56             : #include <unistd.h>
      57             : 
      58             : #include "barrier.h"
      59             : #include "macro.h"
      60             : #include "ring.h"
      61             : #include "util.h"
      62             : #include "signal-util.h"
      63             : #include "pty.h"
      64             : 
      65             : #define PTY_BUFSIZE 4096
      66             : 
      67             : enum {
      68             :         PTY_ROLE_UNKNOWN,
      69             :         PTY_ROLE_PARENT,
      70             :         PTY_ROLE_CHILD,
      71             : };
      72             : 
      73             : struct Pty {
      74             :         unsigned long ref;
      75             :         Barrier barrier;
      76             :         int fd;
      77             :         pid_t child;
      78             :         sd_event_source *fd_source;
      79             :         sd_event_source *child_source;
      80             : 
      81             :         char in_buf[PTY_BUFSIZE];
      82             :         Ring out_buf;
      83             : 
      84             :         pty_event_t event_fn;
      85             :         void *event_fn_userdata;
      86             : 
      87             :         bool needs_requeue : 1;
      88             :         unsigned int role : 2;
      89             : };
      90             : 
      91        1000 : int pty_new(Pty **out) {
      92        2000 :         _pty_unref_ Pty *pty = NULL;
      93             :         int r;
      94             : 
      95        1000 :         assert_return(out, -EINVAL);
      96             : 
      97        1000 :         pty = new0(Pty, 1);
      98        1000 :         if (!pty)
      99           0 :                 return -ENOMEM;
     100             : 
     101        1000 :         pty->ref = 1;
     102        1000 :         pty->fd = -1;
     103        1000 :         pty->barrier = (Barrier) BARRIER_NULL;
     104             : 
     105        1000 :         pty->fd = posix_openpt(O_RDWR | O_NOCTTY | O_CLOEXEC | O_NONBLOCK);
     106        1000 :         if (pty->fd < 0)
     107           0 :                 return -errno;
     108             : 
     109             :         /*
     110             :          * The slave-node is initialized to uid/gid of the caller of
     111             :          * posix_openpt(). Only if devpts is mounted with fixed uid/gid this is
     112             :          * skipped. In that case, grantpt() can overwrite these, but then you
     113             :          * have to be root to use chown() (or a pt_chown helper has to be
     114             :          * present). In those cases grantpt() really does something,
     115             :          * otherwise it's a no-op. We call grantpt() here to try supporting
     116             :          * those cases, even though no-one uses that, I guess. If you need other
     117             :          * access-rights, set them yourself after this call returns (no, this is
     118             :          * not racy, it looks racy, but races regarding your own UID are never
     119             :          * important as an attacker could ptrace you; and the slave-pty is also
     120             :          * still locked).
     121             :          */
     122        1000 :         r = grantpt(pty->fd);
     123        1000 :         if (r < 0)
     124           0 :                 return -errno;
     125             : 
     126        1000 :         r = barrier_create(&pty->barrier);
     127        1000 :         if (r < 0)
     128           0 :                 return r;
     129             : 
     130        1000 :         *out = pty;
     131        1000 :         pty = NULL;
     132        1000 :         return 0;
     133             : }
     134             : 
     135           0 : Pty *pty_ref(Pty *pty) {
     136           0 :         if (!pty || pty->ref < 1)
     137           0 :                 return NULL;
     138             : 
     139           0 :         ++pty->ref;
     140           0 :         return pty;
     141             : }
     142             : 
     143        1000 : Pty *pty_unref(Pty *pty) {
     144        1000 :         if (!pty || pty->ref < 1 || --pty->ref > 0)
     145           0 :                 return NULL;
     146             : 
     147        1000 :         pty_close(pty);
     148        1000 :         pty->child_source = sd_event_source_unref(pty->child_source);
     149        1000 :         barrier_destroy(&pty->barrier);
     150        1000 :         ring_clear(&pty->out_buf);
     151        1000 :         free(pty);
     152             : 
     153        1000 :         return NULL;
     154             : }
     155             : 
     156           0 : Barrier *pty_get_barrier(Pty *pty) {
     157           0 :         assert(pty);
     158           0 :         return &pty->barrier;
     159             : }
     160             : 
     161        3000 : bool pty_is_unknown(Pty *pty) {
     162        3000 :         return pty && pty->role == PTY_ROLE_UNKNOWN;
     163             : }
     164             : 
     165        5817 : bool pty_is_parent(Pty *pty) {
     166        5817 :         return pty && pty->role == PTY_ROLE_PARENT;
     167             : }
     168             : 
     169        1000 : bool pty_is_child(Pty *pty) {
     170        1000 :         return pty && pty->role == PTY_ROLE_CHILD;
     171             : }
     172             : 
     173        2817 : bool pty_has_child(Pty *pty) {
     174        2817 :         return pty_is_parent(pty) && pty->child > 0;
     175             : }
     176             : 
     177        1817 : pid_t pty_get_child(Pty *pty) {
     178        1817 :         return pty_has_child(pty) ? pty->child : -ECHILD;
     179             : }
     180             : 
     181       10999 : bool pty_is_open(Pty *pty) {
     182       10999 :         return pty && pty->fd >= 0;
     183             : }
     184             : 
     185        3999 : int pty_get_fd(Pty *pty) {
     186        3999 :         assert_return(pty, -EINVAL);
     187             : 
     188        3999 :         return pty_is_open(pty) ? pty->fd : -EPIPE;
     189             : }
     190             : 
     191        1000 : int pty_make_child(Pty *pty) {
     192        2000 :         _cleanup_free_ char *slave_name = NULL;
     193             :         int r, fd;
     194             : 
     195        1000 :         assert_return(pty, -EINVAL);
     196        1000 :         assert_return(pty_is_unknown(pty), -EALREADY);
     197             : 
     198        1000 :         r = ptsname_malloc(pty->fd, &slave_name);
     199        1000 :         if (r < 0)
     200           0 :                 return -errno;
     201             : 
     202        1000 :         fd = open(slave_name, O_RDWR | O_CLOEXEC | O_NOCTTY);
     203        1000 :         if (fd < 0)
     204           0 :                 return -errno;
     205             : 
     206        1000 :         safe_close(pty->fd);
     207        1000 :         pty->fd = fd;
     208        1000 :         pty->child = getpid();
     209        1000 :         pty->role = PTY_ROLE_CHILD;
     210        1000 :         barrier_set_role(&pty->barrier, BARRIER_CHILD);
     211             : 
     212        1000 :         return 0;
     213             : }
     214             : 
     215        1000 : int pty_make_parent(Pty *pty, pid_t child) {
     216        1000 :         assert_return(pty, -EINVAL);
     217        1000 :         assert_return(pty_is_unknown(pty), -EALREADY);
     218             : 
     219        1000 :         pty->child = child;
     220        1000 :         pty->role = PTY_ROLE_PARENT;
     221             : 
     222        1000 :         return 0;
     223             : }
     224             : 
     225        1000 : int pty_unlock(Pty *pty) {
     226        1000 :         assert_return(pty, -EINVAL);
     227        1000 :         assert_return(pty_is_unknown(pty) || pty_is_parent(pty), -EINVAL);
     228        1000 :         assert_return(pty_is_open(pty), -ENODEV);
     229             : 
     230        1000 :         return unlockpt(pty->fd) < 0 ? -errno : 0;
     231             : }
     232             : 
     233        1000 : int pty_setup_child(Pty *pty) {
     234             :         struct termios attr;
     235             :         pid_t pid;
     236             :         int r;
     237             : 
     238        1000 :         assert_return(pty, -EINVAL);
     239        1000 :         assert_return(pty_is_child(pty), -EINVAL);
     240        1000 :         assert_return(pty_is_open(pty), -EALREADY);
     241             : 
     242        1000 :         r = reset_signal_mask();
     243        1000 :         if (r < 0)
     244           0 :                 return r;
     245             : 
     246        1000 :         r = reset_all_signal_handlers();
     247        1000 :         if (r < 0)
     248           0 :                 return r;
     249             : 
     250        1000 :         pid = setsid();
     251        1000 :         if (pid < 0 && errno != EPERM)
     252           0 :                 return -errno;
     253             : 
     254        1000 :         r = ioctl(pty->fd, TIOCSCTTY, 0);
     255        1000 :         if (r < 0)
     256           0 :                 return -errno;
     257             : 
     258        1000 :         r = tcgetattr(pty->fd, &attr);
     259        1000 :         if (r < 0)
     260           0 :                 return -errno;
     261             : 
     262             :         /* erase character should be normal backspace, PLEASEEE! */
     263        1000 :         attr.c_cc[VERASE] = 010;
     264             :         /* always set UTF8 flag */
     265        1000 :         attr.c_iflag |= IUTF8;
     266             : 
     267        1000 :         r = tcsetattr(pty->fd, TCSANOW, &attr);
     268        1000 :         if (r < 0)
     269           0 :                 return -errno;
     270             : 
     271        2000 :         if (dup2(pty->fd, STDIN_FILENO) != STDIN_FILENO ||
     272        2000 :             dup2(pty->fd, STDOUT_FILENO) != STDOUT_FILENO ||
     273        1000 :             dup2(pty->fd, STDERR_FILENO) != STDERR_FILENO)
     274           0 :                 return -errno;
     275             : 
     276             :         /* only close FD if it's not a std-fd */
     277        1000 :         pty->fd = (pty->fd > 2) ? safe_close(pty->fd) : -1;
     278             : 
     279        1000 :         return 0;
     280             : }
     281             : 
     282        2000 : void pty_close(Pty *pty) {
     283        2000 :         if (!pty_is_open(pty))
     284        1000 :                 return;
     285             : 
     286        1000 :         pty->fd_source = sd_event_source_unref(pty->fd_source);
     287        1000 :         pty->fd = safe_close(pty->fd);
     288             : }
     289             : 
     290             : /*
     291             :  * Drain input-queue and dispatch data via the event-handler. Returns <0 on
     292             :  * error, 0 if queue is empty and 1 if we couldn't empty the input queue fast
     293             :  * enough and there's still data left.
     294             :  */
     295        2010 : static int pty_dispatch_read(Pty *pty) {
     296             :         unsigned int i;
     297             :         ssize_t len;
     298             :         int r;
     299             : 
     300             :         /*
     301             :          * We're edge-triggered, means we need to read the whole queue. This,
     302             :          * however, might cause us to stall if the writer is faster than we
     303             :          * are. Therefore, try reading as much as 8 times (32KiB) and only
     304             :          * bail out then.
     305             :          */
     306             : 
     307        4009 :         for (i = 0; i < 8; ++i) {
     308        4009 :                 len = read(pty->fd, pty->in_buf, sizeof(pty->in_buf) - 1);
     309        4009 :                 if (len < 0) {
     310        2010 :                         if (errno == EINTR)
     311           0 :                                 continue;
     312             : 
     313        2010 :                         return (errno == EAGAIN) ? 0 : -errno;
     314        1999 :                 } else if (len == 0) {
     315           0 :                         continue;
     316             :                 }
     317             : 
     318             :                 /* set terminating zero for debugging safety */
     319        1999 :                 pty->in_buf[len] = 0;
     320        1999 :                 r = pty->event_fn(pty, pty->event_fn_userdata, PTY_DATA, pty->in_buf, len);
     321        1999 :                 if (r < 0)
     322           0 :                         return r;
     323             :         }
     324             : 
     325             :         /* still data left, make sure we're queued again */
     326           0 :         pty->needs_requeue = true;
     327             : 
     328           0 :         return 1;
     329             : }
     330             : 
     331             : /*
     332             :  * Drain output-queue by writing data to the pty. Returns <0 on error, 0 if the
     333             :  * output queue is empty now and 1 if we couldn't empty the output queue fast
     334             :  * enough and there's still data left.
     335             :  */
     336        4174 : static int pty_dispatch_write(Pty *pty) {
     337             :         struct iovec vec[2];
     338             :         unsigned int i;
     339             :         ssize_t len;
     340             :         size_t num;
     341             : 
     342             :         /*
     343             :          * Same as pty_dispatch_read(), we're edge-triggered so we need to call
     344             :          * write() until either all data is written or it returns EAGAIN. We
     345             :          * call it twice and if it still writes successfully, we reschedule.
     346             :          */
     347             : 
     348        5174 :         for (i = 0; i < 2; ++i) {
     349        5174 :                 num = ring_peek(&pty->out_buf, vec);
     350        5174 :                 if (num < 1)
     351        4174 :                         return 0;
     352             : 
     353        1000 :                 len = writev(pty->fd, vec, (int)num);
     354        1000 :                 if (len < 0) {
     355           0 :                         if (errno == EINTR)
     356           0 :                                 continue;
     357             : 
     358           0 :                         return (errno == EAGAIN) ? 1 : -errno;
     359        1000 :                 } else if (len == 0) {
     360           0 :                         continue;
     361             :                 }
     362             : 
     363        1000 :                 ring_pull(&pty->out_buf, (size_t)len);
     364             :         }
     365             : 
     366             :         /* still data left, make sure we're queued again */
     367           0 :         if (ring_get_size(&pty->out_buf) > 0) {
     368           0 :                 pty->needs_requeue = true;
     369           0 :                 return 1;
     370             :         }
     371             : 
     372           0 :         return 0;
     373             : }
     374             : 
     375        4174 : static int pty_fd_fn(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
     376        4174 :         Pty *pty = userdata;
     377        4174 :         int r_hup = 0, r_write = 0, r_read = 0, r;
     378             : 
     379             :         /*
     380             :          * Whenever we encounter I/O errors, we have to make sure to drain the
     381             :          * input queue first, before we handle any HUP. A child might send us
     382             :          * a message and immediately close the queue. We must not handle the
     383             :          * HUP first or we loose data.
     384             :          * Therefore, if we read a message successfully, we always return
     385             :          * success and wait for the next event-loop iteration. Furthermore,
     386             :          * whenever there is a write-error, we must try reading from the input
     387             :          * queue even if EPOLLIN is not set. The input might have arrived in
     388             :          * between epoll_wait() and write(). Therefore, write-errors are only
     389             :          * ever handled if the input-queue is empty. In all other cases they
     390             :          * are ignored until either reading fails or the input queue is empty.
     391             :          */
     392             : 
     393        4174 :         if (revents & (EPOLLHUP | EPOLLERR))
     394         997 :                 r_hup = -EPIPE;
     395             : 
     396        4174 :         if (revents & EPOLLOUT)
     397        4174 :                 r_write = pty_dispatch_write(pty);
     398             : 
     399             :         /* Awesome! Kernel signals HUP without IN but queues are not empty.. */
     400        4174 :         if ((revents & EPOLLIN) || r_hup < 0 || r_write < 0) {
     401        2010 :                 r_read = pty_dispatch_read(pty);
     402        2010 :                 if (r_read > 0)
     403           0 :                         return 0; /* still data left to fetch next round */
     404             :         }
     405             : 
     406        4174 :         if (r_hup < 0 || r_write < 0 || r_read < 0) {
     407             :                 /* PTY closed and input-queue drained */
     408        1000 :                 pty_close(pty);
     409        1000 :                 r = pty->event_fn(pty, pty->event_fn_userdata, PTY_HUP, NULL, 0);
     410        1000 :                 if (r < 0)
     411           0 :                         return r;
     412             :         }
     413             : 
     414        4174 :         return 0;
     415             : }
     416             : 
     417        4357 : static int pty_fd_prepare_fn(sd_event_source *source, void *userdata) {
     418        4357 :         Pty *pty = userdata;
     419             :         int r;
     420             : 
     421        4357 :         if (pty->needs_requeue) {
     422             :                 /*
     423             :                  * We're edge-triggered. In case we couldn't handle all events
     424             :                  * or in case new write-data is queued, we set needs_requeue.
     425             :                  * Before going asleep, we set the io-events *again*. sd-event
     426             :                  * notices that we're edge-triggered and forwards the call to
     427             :                  * the kernel even if the events didn't change. The kernel will
     428             :                  * check the events and re-queue us on the ready queue in case
     429             :                  * an event is pending.
     430             :                  */
     431        1000 :                 r = sd_event_source_set_io_events(source, EPOLLHUP | EPOLLERR | EPOLLIN | EPOLLOUT | EPOLLET);
     432        1000 :                 if (r >= 0)
     433        1000 :                         pty->needs_requeue = false;
     434             :         }
     435             : 
     436        4357 :         return 0;
     437             : }
     438             : 
     439        1000 : static int pty_child_fn(sd_event_source *source, const siginfo_t *si, void *userdata) {
     440        1000 :         Pty *pty = userdata;
     441             :         int r;
     442             : 
     443        1000 :         pty->child = 0;
     444             : 
     445        1000 :         r = pty->event_fn(pty, pty->event_fn_userdata, PTY_CHILD, si, sizeof(*si));
     446        1000 :         if (r < 0)
     447           0 :                 return r;
     448             : 
     449        1000 :         return 0;
     450             : }
     451             : 
     452        1000 : int pty_attach_event(Pty *pty, sd_event *event, pty_event_t event_fn, void *event_fn_userdata) {
     453             :         int r;
     454             : 
     455        1000 :         assert_return(pty, -EINVAL);
     456        1000 :         assert_return(event, -EINVAL);
     457        1000 :         assert_return(event_fn, -EINVAL);
     458        1000 :         assert_return(pty_is_parent(pty), -EINVAL);
     459             : 
     460        1000 :         pty_detach_event(pty);
     461             : 
     462        1000 :         if (pty_is_open(pty)) {
     463        1000 :                 r = sd_event_add_io(event,
     464             :                                     &pty->fd_source,
     465             :                                     pty->fd,
     466             :                                     EPOLLHUP | EPOLLERR | EPOLLIN | EPOLLOUT | EPOLLET,
     467             :                                     pty_fd_fn,
     468             :                                     pty);
     469        1000 :                 if (r < 0)
     470           0 :                         goto error;
     471             : 
     472        1000 :                 r = sd_event_source_set_prepare(pty->fd_source, pty_fd_prepare_fn);
     473        1000 :                 if (r < 0)
     474           0 :                         goto error;
     475             :         }
     476             : 
     477        1000 :         if (pty_has_child(pty)) {
     478        1000 :                 r = sd_event_add_child(event,
     479             :                                        &pty->child_source,
     480             :                                        pty->child,
     481             :                                        WEXITED,
     482             :                                        pty_child_fn,
     483             :                                        pty);
     484        1000 :                 if (r < 0)
     485           0 :                         goto error;
     486             :         }
     487             : 
     488        1000 :         pty->event_fn = event_fn;
     489        1000 :         pty->event_fn_userdata = event_fn_userdata;
     490             : 
     491        1000 :         return 0;
     492             : 
     493             : error:
     494           0 :         pty_detach_event(pty);
     495           0 :         return r;
     496             : }
     497             : 
     498        1000 : void pty_detach_event(Pty *pty) {
     499        1000 :         if (!pty)
     500           0 :                 return;
     501             : 
     502        1000 :         pty->child_source = sd_event_source_unref(pty->child_source);
     503        1000 :         pty->fd_source = sd_event_source_unref(pty->fd_source);
     504        1000 :         pty->event_fn = NULL;
     505        1000 :         pty->event_fn_userdata = NULL;
     506             : }
     507             : 
     508        1000 : int pty_write(Pty *pty, const void *buf, size_t size) {
     509             :         bool was_empty;
     510             :         int r;
     511             : 
     512        1000 :         assert_return(pty, -EINVAL);
     513        1000 :         assert_return(pty_is_open(pty), -ENODEV);
     514        1000 :         assert_return(pty_is_parent(pty), -ENODEV);
     515             : 
     516        1000 :         if (size < 1)
     517           0 :                 return 0;
     518             : 
     519             :         /*
     520             :          * Push @buf[0..@size] into the output ring-buffer. In case the
     521             :          * ring-buffer wasn't empty beforehand, we're already waiting for
     522             :          * EPOLLOUT and we're done. If it was empty, we have to re-queue the
     523             :          * FD for EPOLLOUT as we're edge-triggered and wouldn't get any new
     524             :          * EPOLLOUT event.
     525             :          */
     526             : 
     527        1000 :         was_empty = ring_get_size(&pty->out_buf) < 1;
     528             : 
     529        1000 :         r = ring_push(&pty->out_buf, buf, size);
     530        1000 :         if (r < 0)
     531           0 :                 return r;
     532             : 
     533        1000 :         if (was_empty)
     534        1000 :                 pty->needs_requeue = true;
     535             : 
     536        1000 :         return 0;
     537             : }
     538             : 
     539           0 : int pty_signal(Pty *pty, int sig) {
     540           0 :         assert_return(pty, -EINVAL);
     541           0 :         assert_return(pty_is_open(pty), -ENODEV);
     542           0 :         assert_return(pty_is_parent(pty), -ENODEV);
     543             : 
     544           0 :         return ioctl(pty->fd, TIOCSIG, sig) < 0 ? -errno : 0;
     545             : }
     546             : 
     547        1000 : int pty_resize(Pty *pty, unsigned short term_width, unsigned short term_height) {
     548        1000 :         struct winsize ws = {
     549             :                 .ws_col = term_width,
     550             :                 .ws_row = term_height,
     551             :         };
     552             : 
     553        1000 :         assert_return(pty, -EINVAL);
     554        1000 :         assert_return(pty_is_open(pty), -ENODEV);
     555        1000 :         assert_return(pty_is_parent(pty), -ENODEV);
     556             : 
     557             :         /*
     558             :          * This will send SIGWINCH to the pty slave foreground process group.
     559             :          * We will also get one, but we don't need it.
     560             :          */
     561        1000 :         return ioctl(pty->fd, TIOCSWINSZ, &ws) < 0 ? -errno : 0;
     562             : }
     563             : 
     564        1000 : pid_t pty_fork(Pty **out, sd_event *event, pty_event_t event_fn, void *event_fn_userdata, unsigned short initial_term_width, unsigned short initial_term_height) {
     565        3000 :         _pty_unref_ Pty *pty = NULL;
     566             :         int r;
     567             :         pid_t pid;
     568             : 
     569        1000 :         assert_return(out, -EINVAL);
     570        1000 :         assert_return((event && event_fn) || (!event && !event_fn), -EINVAL);
     571             : 
     572        1000 :         r = pty_new(&pty);
     573        1000 :         if (r < 0)
     574           0 :                 return r;
     575             : 
     576        1000 :         r = pty_unlock(pty);
     577        1000 :         if (r < 0)
     578           0 :                 return r;
     579             : 
     580        1000 :         pid = fork();
     581        2000 :         if (pid < 0)
     582           0 :                 return -errno;
     583             : 
     584        2000 :         if (pid == 0) {
     585             :                 /* child */
     586             : 
     587        1000 :                 r = pty_make_child(pty);
     588        1000 :                 if (r < 0)
     589           0 :                         _exit(-r);
     590             : 
     591        1000 :                 r = pty_setup_child(pty);
     592        1000 :                 if (r < 0)
     593           0 :                         _exit(-r);
     594             : 
     595             :                 /* sync with parent */
     596        1000 :                 if (!barrier_place_and_sync(&pty->barrier))
     597           0 :                         _exit(1);
     598             : 
     599             :                 /* fallthrough and return the child's PTY object */
     600             :         } else {
     601             :                 /* parent */
     602             : 
     603        1000 :                 r = pty_make_parent(pty, pid);
     604        1000 :                 if (r < 0)
     605           0 :                         goto parent_error;
     606             : 
     607        1000 :                 r = pty_resize(pty, initial_term_width, initial_term_height);
     608        1000 :                 if (r < 0)
     609           0 :                         goto parent_error;
     610             : 
     611        1000 :                 if (event) {
     612        1000 :                         r = pty_attach_event(pty, event, event_fn, event_fn_userdata);
     613        1000 :                         if (r < 0)
     614           0 :                                 goto parent_error;
     615             :                 }
     616             : 
     617             :                 /* sync with child */
     618        1000 :                 if (!barrier_place_and_sync(&pty->barrier)) {
     619           0 :                         r = -ECHILD;
     620           0 :                         goto parent_error;
     621             :                 }
     622             : 
     623             :                 /* fallthrough and return the parent's PTY object */
     624             :         }
     625             : 
     626        2000 :         *out = pty;
     627        2000 :         pty = NULL;
     628        2000 :         return pid;
     629             : 
     630             : parent_error:
     631           0 :         barrier_abort(&pty->barrier);
     632           0 :         waitpid(pty->child, NULL, 0);
     633           0 :         pty->child = 0;
     634           0 :         return r;
     635             : }

Generated by: LCOV version 1.11