c1a3ac67f1f5bdb08488e3d4390c521c8a2a3c58
[vte.git] / gnome-pty-helper / gnome-login-support.c
1 /*
2  * gnome-login-support.c:
3  *    Replacement for systems that lack login_tty, open_pty and forkpty
4  *
5  * Author:
6  *    Miguel de Icaza (miguel@gnu.org)
7  *
8  *
9  */
10 #include <config.h>
11 #include <termios.h>
12 #include <unistd.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <fcntl.h>
16 #include <string.h>
17 #include <sys/ioctl.h>
18 #include <termios.h>
19 #include <stdlib.h>
20 #include <sys/stat.h>
21 #include <errno.h>
22 #include <grp.h>
23 #include <sys/types.h>
24 #include "gnome-login-support.h"
25
26 /*
27  * HAVE_OPENPTY => HAVE_FORKPTY
28  */
29
30 #ifndef HAVE_LOGIN_TTY
31 int
32 login_tty (int fd)
33 {
34         pid_t pid = getpid ();
35
36         /* Create the session */
37         setsid ();
38
39 #ifdef TIOCSCTTY
40         if (ioctl (fd, TIOCSCTTY, 0) == -1)
41                 return -1;
42 #else /* !TIOCSTTY */
43         /* Hackery to set controlling tty on SVR4 -
44            on SVR4 the first terminal we open after sesid()
45            becomes our controlling terminal, thus we must
46            find the name of, open, and re-close the tty
47            since we already have it open at this point. */
48         {
49                 char *ctty;
50                 int ct_fdes;
51
52                 ctty = ttyname(fd);
53                 ct_fdes = open(ctty, O_RDWR);
54                 close(ct_fdes);
55         }
56 #endif /* !TIOCSTTY */
57
58 #if defined (_POSIX_VERSION) || defined (__svr4__)
59         tcsetpgrp (0, pid);
60 #elif defined (TIOCSPGRP)
61         ioctl (0, TIOCSPGRP, &pid);
62 #endif
63
64         dup2 (fd, 0);
65         dup2 (fd, 1);
66         dup2 (fd, 2);
67         if (fd > 2)
68                 close (fd);
69
70         return 0;
71 }
72 #endif
73
74 #ifndef HAVE_OPENPTY
75 static int
76 pty_open_master_bsd (char *pty_name, int *used_bsd)
77 {
78         int pty_master;
79         char *ptr1, *ptr2;
80
81         *used_bsd = 1;
82
83         strcpy (pty_name, "/dev/ptyXX");
84         for (ptr1 = "pqrstuvwxyzPQRST"; *ptr1; ++ptr1)
85         {
86                 pty_name [8] = *ptr1;
87                 for (ptr2 = "0123456789abcdef"; *ptr2; ++ptr2)
88                 {
89                         pty_name [9] = *ptr2;
90
91                         /* Try to open master */
92                         if ((pty_master = open (pty_name, O_RDWR)) == -1) {
93                                 if (errno == ENOENT)  /* Different from EIO */
94                                         return -1;    /* Out of pty devices */
95                                 else
96                                         continue;      /* Try next pty device */
97                         }
98                         pty_name [5] = 't';            /* Change "pty" to "tty" */
99                         if (access (pty_name, (R_OK | W_OK))){
100                                 close (pty_master);
101                                 pty_name [5] = 'p';
102                                 continue;
103                         }
104                         return pty_master;
105                 }
106         }
107         return -1;  /* Ran out of pty devices */
108 }
109
110 static int
111 pty_open_slave_bsd (const char *pty_name)
112 {
113         int pty_slave;
114         struct group *group_info = getgrnam ("tty");
115
116         if (group_info != NULL)
117         {
118                 /* The following two calls will only succeed if we are root */
119
120                 chown (pty_name, getuid (), group_info->gr_gid);
121                 chmod (pty_name, S_IRUSR | S_IWUSR | S_IWGRP);
122         }
123         else
124         {
125                 chown (pty_name, getuid (), -1);
126                 chmod (pty_name, S_IRUSR | S_IWUSR | S_IWGRP);
127         }
128
129 #ifdef HAVE_REVOKE
130         revoke (pty_name);
131 #endif
132
133         if ((pty_slave = open (pty_name, O_RDWR)) == -1){
134                 return -1;
135         }
136
137         return pty_slave;
138 }
139
140 /* SystemVish pty opening */
141 #if defined (HAVE_GRANTPT)
142
143 #ifdef HAVE_STROPTS_H
144 #    include <stropts.h>
145 #endif
146
147 static int
148 pty_open_slave (const char *pty_name)
149 {
150         int pty_slave = open (pty_name, O_RDWR);
151
152         if (pty_slave == -1)
153                 return -1;
154
155 #ifdef HAVE_STROPTS_H
156 #if !defined(__osf__)
157         if (!ioctl (pty_slave, I_FIND, "ptem"))
158                 if (ioctl (pty_slave, I_PUSH, "ptem") == -1){
159                         close (pty_slave);
160                         return -1;
161                 }
162
163     if (!ioctl (pty_slave, I_FIND, "ldterm"))
164             if (ioctl (pty_slave, I_PUSH, "ldterm") == -1){
165                     close (pty_slave);
166                     return -1;
167             }
168
169 #if !defined(sgi) && !defined(__sgi)
170     if (!ioctl (pty_slave, I_FIND, "ttcompat"))
171             if (ioctl (pty_slave, I_PUSH, "ttcompat") == -1)
172             {
173                     perror ("ioctl (pty_slave, I_PUSH, \"ttcompat\")");
174                     close (pty_slave);
175                     return -1;
176             }
177 #endif /* sgi || __sgi */
178 #endif /* __osf__ */
179 #endif /* HAVE_STROPTS_H */
180
181     return pty_slave;
182 }
183
184 static int
185 pty_open_master (char *pty_name, int *used_bsd)
186 {
187         int pty_master;
188         char *slave_name;
189
190         strcpy (pty_name, "/dev/ptmx");
191
192         pty_master = open (pty_name, O_RDWR);
193
194         if ((pty_master == -1) && (errno == ENOENT)) {
195                 strcpy (pty_name, "/dev/ptc"); /* AIX */
196                 pty_master = open (pty_name, O_RDWR);
197         }
198
199         /*
200          * Try BSD open, this is needed for Linux which
201          * might have Unix98 devices but no kernel support
202          * for those.
203          */
204         if (pty_master == -1) {
205                 *used_bsd = 1;
206                 return pty_open_master_bsd (pty_name, used_bsd);
207         }
208         *used_bsd = 0;
209
210         if (grantpt (pty_master) == -1 || unlockpt (pty_master) == -1) {
211                 close (pty_master);
212                 return -1;
213         }
214         if ((slave_name = ptsname (pty_master)) == NULL){
215                 close (pty_master);
216                 return -1;
217         }
218         strcpy (pty_name, slave_name);
219         return pty_master;
220 }
221 #else
222 #    define pty_open_master pty_open_master_bsd
223 #    define pty_open_slave  pty_open_slave_bsd
224 #endif
225
226 int
227 openpty (int *master_fd, int *slave_fd, char *name,
228          struct termios *termp, struct winsize *winp)
229 {
230         int pty_master, pty_slave, used_bsd = 0;
231         struct group *group_info;
232         char line [256];
233
234         pty_master = pty_open_master (line, &used_bsd);
235         fcntl (pty_master, F_SETFD, FD_CLOEXEC);
236
237         if (pty_master == -1)
238                 return -1;
239
240         group_info = getgrnam ("tty");
241
242         if (group_info != NULL){
243                 chown (line, getuid (), group_info->gr_gid);
244                 chmod (line, S_IRUSR | S_IWUSR | S_IWGRP);
245         } else {
246                 chown (line, getuid (), -1);
247                 chmod (line, S_IRUSR | S_IWUSR | S_IWGRP);
248         }
249
250 #ifdef HAVE_REVOKE
251         revoke (line);
252 #endif
253
254         /* Open slave side */
255         if (used_bsd)
256                 pty_slave = pty_open_slave_bsd (line);
257         else
258                 pty_slave = pty_open_slave (line);
259
260         if (pty_slave == -1){
261                 close (pty_master);
262
263                 errno = ENOENT;
264                 return -1;
265         }
266         fcntl (pty_slave, F_SETFD, FD_CLOEXEC);
267
268         *master_fd = pty_master;
269         *slave_fd  = pty_slave;
270
271         if (termp)
272                 tcsetattr (pty_slave, TCSAFLUSH, termp);
273
274         if (winp)
275                 ioctl (pty_slave, TIOCSWINSZ, winp);
276
277         if (name)
278                 strcpy (name, line);
279
280         return 0;
281 }
282
283 pid_t
284 forkpty (int *master_fd, char *name, struct termios *termp, struct winsize *winp)
285 {
286         int master, slave;
287         pid_t pid;
288
289         if (openpty (&master, &slave, name, termp, winp) == -1)
290                 return -1;
291
292         pid = fork ();
293
294         if (pid == -1)
295                 return -1;
296
297         /* Child */
298         if (pid == 0){
299                 close (master);
300                 login_tty (slave);
301         } else {
302                 *master_fd = master;
303                 close (slave);
304         }
305
306         return pid;
307 }
308 #endif /* HAVE_OPENPTY */
309
310 int
311 n_read (int fd, void *buf, int count)
312 {
313         int n = 0, i;
314         char *buffer;
315
316         buffer = (char*) buf;
317         while (n < count) {
318                 i = read (fd, buffer + n, count - n);
319                 switch (i) {
320                 case -1:
321                         switch (errno) {
322                         case EINTR:
323                         case EAGAIN:
324 #ifdef ERESTART
325                         case ERESTART:
326 #endif
327                                 /* suppress these errors */
328                                 break;
329                         default:
330                                 return -1;
331                                 break;
332                         }
333                         break;
334                 case 0:
335                         return n;
336                         break;
337                 default:
338                         n += i;
339                         break;
340                 }
341         }
342
343         return n;
344 }
345
346 int
347 n_write (int fd, const void *buf, int count)
348 {
349         int n = 0, i;
350         const char *buffer;
351
352         buffer = (char*) buf;
353         while (n < count) {
354                 i = write (fd, buffer + n, count - n);
355                 switch (i) {
356                 case -1:
357                         switch (errno) {
358                         case EINTR:
359                         case EAGAIN:
360 #ifdef ERESTART
361                         case ERESTART:
362 #endif
363                                 /* suppress these errors */
364                                 break;
365                         default:
366                                 return -1;
367                                 break;
368                         }
369                         break;
370                 case 0:
371                         return n;
372                         break;
373                 default:
374                         n += i;
375                         break;
376                 }
377         }
378
379         return n;
380 }