don't depend on PATH_MAX being available and correct (#109805). don't
[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, 6)){
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         /*
195          * Try BSD open, this is needed for Linux which
196          * might have Unix98 devices but no kernel support
197          * for those.
198          */
199         if (pty_master == -1){
200                 *used_bsd = 1;
201                 return pty_open_master_bsd (pty_name, used_bsd);
202         }
203         *used_bsd = 0;
204
205         if (grantpt  (pty_master) == -1 || unlockpt (pty_master) == -1){
206                 close (pty_master);
207                 return -1;
208         }
209         if ((slave_name = ptsname (pty_master)) == NULL){
210                 close (pty_master);
211                 return -1;
212         }
213         strcpy (pty_name, slave_name);
214         return pty_master;
215 }
216 #else
217 #    define pty_open_master pty_open_master_bsd
218 #    define pty_open_slave  pty_open_slave_bsd
219 #endif
220
221 int
222 openpty (int *master_fd, int *slave_fd, char *name, struct termios *termp, struct winsize *winp)
223 {
224         int pty_master, pty_slave, used_bsd = 0;
225         struct group *group_info;
226         char line [256];
227
228         pty_master = pty_open_master (line, &used_bsd);
229         fcntl (pty_master, F_SETFD, FD_CLOEXEC);
230
231         if (pty_master == -1)
232                 return -1;
233
234         group_info = getgrnam ("tty");
235         
236         if (group_info != NULL){
237                 chown (line, getuid (), group_info->gr_gid);
238                 chmod (line, S_IRUSR | S_IWUSR | S_IWGRP);
239         }
240         else
241         {
242                 chown (line, getuid (), -1);
243                 chmod (line, S_IRUSR | S_IWUSR | S_IWGRP);
244         }
245
246 #ifdef HAVE_REVOKE
247         revoke (line);
248 #endif
249         
250         /* Open slave side */
251         if (used_bsd)
252                 pty_slave = pty_open_slave_bsd (line);
253         else
254                 pty_slave = pty_open_slave (line);
255         
256         if (pty_slave == -1){
257                 close (pty_master);
258
259                 errno = ENOENT;
260                 return -1;
261         }
262         fcntl (pty_slave, F_SETFD, FD_CLOEXEC);
263
264         *master_fd = pty_master;
265         *slave_fd  = pty_slave;
266
267         if (termp)
268                 tcsetattr (pty_slave, TCSAFLUSH, termp);
269
270         if (winp)
271                 ioctl (pty_slave, TIOCSWINSZ, winp);
272         
273         if (name)
274                 strcpy (name, line);
275
276         return 0;
277 }
278
279 pid_t
280 forkpty (int *master_fd, char *name, struct termios *termp, struct winsize *winp)
281 {
282         int master, slave;
283         pid_t pid;
284         
285         if (openpty (&master, &slave, name, termp, winp) == -1)
286                 return -1;
287
288         pid = fork ();
289
290         if (pid == -1)
291                 return -1;
292
293         /* Child */
294         if (pid == 0){
295                 close (master);
296                 login_tty (slave);
297         } else {
298                 *master_fd = master;
299                 close (slave);
300         }
301         
302         return pid;
303 }
304 #endif /* HAVE_OPENPTY */
305
306 int
307 n_read (int fd, void *buf, int n)
308 {
309         int left, nread;
310         char *ptr, *buffer;
311
312         buffer = (char*) buf;
313         ptr = buffer;
314         while (ptr < buffer + n) {
315                 nread = read (fd, ptr, n - (ptr - buffer));
316                 switch (nread) {
317                 case -1:
318                         switch (errno) {
319                         case EINTR:
320                         case EAGAIN:
321                                 /* suppress the error */
322                                 break;
323                         default:
324                                 return -1;
325                                 break;
326                         }
327                         break;
328                 case 0:
329                         /* EOF */
330                         break;
331                 }
332                 if (nread > 0) {
333                         ptr += nread;
334                 } else {
335                         break;
336                 }
337         }
338         
339         return (ptr - ((char*)buffer));
340 }
341