Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[dragonfly.git] / crypto / kerberosIV / appl / bsd / forkpty.c
1 /*
2  * Copyright (c) 1995, 1996, 1997 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  * 
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  * 
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #include "bsd_locl.h"
35
36 #ifndef HAVE_FORKPTY
37
38 RCSID("$Id: forkpty.c,v 1.57 1999/12/02 16:58:28 joda Exp $");
39
40 /* Only CRAY is known to have problems with forkpty(). */
41 #if defined(CRAY)
42 static int forkpty_ok = 0;
43 #else
44 static int forkpty_ok = 1;
45 #endif
46
47 #ifndef HAVE_PTSNAME
48 static char *ptsname(int fd)
49 {
50 #ifdef HAVE_TTYNAME
51   return ttyname(fd);
52 #else
53   return NULL;
54 #endif
55 }
56 #endif
57
58 #ifndef HAVE_GRANTPT
59 #define grantpt(fdm) (0)
60 #endif
61
62 #ifndef HAVE_UNLOCKPT
63 #define unlockpt(fdm) (0)
64 #endif
65
66 #ifndef HAVE_VHANGUP
67 #define vhangup() (0)
68 #endif
69
70 #ifndef HAVE_REVOKE
71 static
72 void
73 revoke(char *line)
74 {
75     int slave;
76     RETSIGTYPE (*ofun)();
77
78     if ( (slave = open(line, O_RDWR)) < 0)
79         return;
80     
81     ofun = signal(SIGHUP, SIG_IGN);
82     vhangup();
83     signal(SIGHUP, ofun);
84     /*
85      * Some systems (atleast SunOS4) want to have the slave end open
86      * at all times to prevent a race in the child. Login will close
87      * it so it should really not be a problem. However for the
88      * paranoid we use the close on exec flag so it will only be open
89      * in the parent. Additionally since this will be the controlling
90      * tty of rlogind the final vhangup() in rlogind should hangup all
91      * processes. A working revoke would of course have been prefered
92      * though (sigh).
93      */
94     fcntl(slave, F_SETFD, 1);
95     /* close(slave); */
96 }
97 #endif
98
99
100 static int pty_major, pty_minor;
101
102 static void
103 pty_scan_start(void)
104 {
105     pty_major = -1;
106     pty_minor = 0;
107 }
108
109 static char *bsd_1 = "0123456789abcdefghijklmnopqrstuv";
110 /* there are many more */
111 static char *bsd_2 = "pqrstuvwxyzabcdefghijklmnoABCDEFGHIJKLMNOPQRSTUVWXYZ";
112
113 static int
114 pty_scan_next(char *buf, size_t sz)
115 {
116 #ifdef CRAY
117     if(++pty_major >= sysconf(_SC_CRAY_NPTY))
118         return -1;
119     snprintf(buf, sz, "/dev/pty/%03d", pty_major);
120 #else
121     if(++pty_major == strlen(bsd_1)){
122         pty_major = 0;
123         if(++pty_minor == strlen(bsd_2))
124             return -1;
125     }
126 #ifdef __hpux
127     snprintf(buf, sz, "/dev/ptym/pty%c%c", bsd_2[pty_major], bsd_1[pty_minor]);
128 #else
129     snprintf(buf, sz, "/dev/pty%c%c", bsd_2[pty_major], bsd_1[pty_minor]);
130 #endif /* __hpux */
131 #endif /* CRAY */
132     return 0;
133 }
134
135 static void
136 pty_scan_tty(char *buf, size_t sz)
137 {
138 #ifdef CRAY
139     snprintf(buf, sz, "/dev/ttyp%03d", pty_major);
140 #elif defined(__hpux)
141     snprintf(buf, sz, "/dev/pty/tty%c%c", bsd_2[pty_major], bsd_1[pty_minor]);
142 #else
143     snprintf(buf, sz, "/dev/tty%c%c", bsd_2[pty_major], bsd_1[pty_minor]);
144 #endif
145 }
146
147 static int
148 ptym_open_streams_flavor(char *pts_name,
149                          size_t pts_name_sz,
150                          int *streams_pty)
151 {
152     /* Try clone device master ptys */
153     const char *const clone[] = { "/dev/ptc", "/dev/ptmx",
154                                   "/dev/ptm", "/dev/ptym/clone", 0 };
155     int fdm;
156     const char *const *q;
157     
158     for (q = clone; *q; q++) {
159         fdm = open(*q, O_RDWR);
160         if (fdm >= 0)
161             break;
162     }
163     if (fdm >= 0) {
164         char *ptr1;
165         if ((ptr1 = ptsname(fdm)) != NULL) /* Get slave's name */
166             /* Return name of slave */  
167             strlcpy(pts_name, ptr1, pts_name_sz);
168         else {
169             close(fdm);
170             return(-4);
171         }
172         if (grantpt(fdm) < 0) { /* Grant access to slave */
173             close(fdm);
174             return(-2);
175         }
176         if (unlockpt(fdm) < 0) {        /* Clear slave's lock flag */
177             close(fdm);
178             return(-3);
179         }
180         return(fdm);                    /* return fd of master */
181     }
182     return -1;
183 }
184
185 static int
186 ptym_open_bsd_flavor(char *pts_name, size_t pts_name_sz, int *streams_pty)
187 {
188     int fdm;
189     char ptm[MaxPathLen];
190
191     pty_scan_start();
192
193     while (pty_scan_next(ptm, sizeof(ptm)) != -1) {
194         fdm = open(ptm, O_RDWR);
195         if (fdm < 0)
196             continue;
197 #if SunOS == 40
198         /* Avoid a bug in SunOS4 ttydriver */
199         if (fdm > 0) {
200             int pgrp;
201             if ((ioctl(fdm, TIOCGPGRP, &pgrp) == -1)
202                 && (errno == EIO))
203                 /* All fine */;
204             else {
205                 close(fdm);
206                 continue;
207             }
208         }
209 #endif
210         pty_scan_tty(pts_name, sizeof(ptm));
211 #if CRAY
212         /* this is some magic from the telnet code */
213         {
214             struct stat sb;
215             if(stat(pts_name, &sb) < 0) {
216                 close(fdm);
217                 continue;
218             }
219             if(sb.st_uid || sb.st_gid || sb.st_mode != 0600) {
220                 chown(pts_name, 0, 0);
221                 chmod(pts_name, 0600);
222                 close(fdm);
223                 fdm = open(ptm, 2);
224                 if (fdm < 0)
225                     continue;
226             }
227         }
228         /*
229          * Now it should be safe...check for accessability.
230          */
231         if (access(pts_name, 6) != 0){
232             /* no tty side to pty so skip it */
233             close(fdm);
234             continue;
235         }
236 #endif
237         return fdm;     /* All done! */
238     }
239     
240     /* We failed to find BSD style pty */
241     errno = ENOENT;
242     return -1;
243 }
244
245 /*
246  *
247  * Open a master pty either using the STREAM flavor or the BSD flavor.
248  * Depending on if there are any free ptys in the different classes we
249  * need to try both. Normally try STREAMS first and then BSD.
250  *
251  * Kludge alert: Under HP-UX 10 and perhaps other systems STREAM ptys
252  * doesn't get initialized properly so we try them in different order
253  * until the problem has been resolved.
254  *
255  */
256 static int
257 ptym_open(char *pts_name, size_t pts_name_sz, int *streams_pty)
258 {
259     int fdm;
260
261 #ifdef HAVE__GETPTY
262     {
263         char *p = _getpty(&fdm, O_RDWR, 0600, 1);
264         if (p) {
265             *streams_pty = 1;
266             strlcpy (pts_name, p, pts_name_sz);
267             return fdm;
268         }
269     }
270 #endif
271
272 #ifdef STREAMSPTY
273     fdm = ptym_open_streams_flavor(pts_name, pts_name_sz, streams_pty);
274     if (fdm >= 0)
275       {
276         *streams_pty = 1;
277         return fdm;
278       }
279 #endif
280     
281     fdm = ptym_open_bsd_flavor(pts_name, pts_name_sz, streams_pty);
282     if (fdm >= 0)
283       {
284         *streams_pty = 0;
285         return fdm;
286       }
287
288 #ifndef STREAMSPTY
289     fdm = ptym_open_streams_flavor(pts_name, pts_name_sz, streams_pty);
290     if (fdm >= 0)
291       {
292         *streams_pty = 1;
293         return fdm;
294       }
295 #endif
296     
297     return -1;
298 }
299
300 static int
301 maybe_push_modules(int fd, char **modules)
302 {
303 #ifdef I_PUSH
304   char **p;
305   int err;
306
307   for(p=modules; *p; p++){
308     err=ioctl(fd, I_FIND, *p);
309     if(err == 1)
310       break;
311     if(err < 0 && errno != EINVAL)
312       return -17;
313     /* module not pushed or does not exist */
314   }
315   /* p points to null or to an already pushed module, now push all
316      modules before this one */
317
318   for(p--; p >= modules; p--){
319     err = ioctl(fd, I_PUSH, *p);
320     if(err < 0 && errno != EINVAL)
321       return -17;
322   }
323 #endif
324   return 0;
325 }
326
327 static int
328 ptys_open(int fdm, char *pts_name, int streams_pty)
329 {
330     int fds;
331
332     if (streams_pty) {
333         /* Streams style slave ptys */
334         if ( (fds = open(pts_name, O_RDWR)) < 0) {
335             close(fdm);
336             return(-5);
337         }
338
339         {
340           char *ttymodules[] = { "ttcompat", "ldterm", "ptem", NULL };
341           char *ptymodules[] = { "pckt", NULL };
342           
343           if(maybe_push_modules(fds, ttymodules)<0){
344             close(fdm);
345             close(fds);
346             return -6;
347           }
348           if(maybe_push_modules(fdm, ptymodules)<0){
349             close(fdm);
350             close(fds);
351             return -7;
352           }
353         }
354     } else {
355         /* BSD style slave ptys */
356         struct group *grptr;
357         int gid;
358         if ( (grptr = getgrnam("tty")) != NULL)
359             gid = grptr->gr_gid;
360         else
361             gid = -1;   /* group tty is not in the group file */
362
363         /* Grant access to slave */
364         if (chown(pts_name, getuid(), gid) < 0)
365           fatal(0, "chown slave tty failed", 1);
366         if (chmod(pts_name, S_IRUSR | S_IWUSR | S_IWGRP) < 0)
367           fatal(0, "chmod slave tty failed", 1);
368
369         if ( (fds = open(pts_name, O_RDWR)) < 0) {
370             close(fdm);
371             return(-1);
372         }
373     }
374     return(fds);
375 }
376
377 int
378 forkpty_truncate(int *ptrfdm,
379                  char *slave_name,
380                  size_t slave_name_sz,
381                  struct termios *slave_termios,
382                  struct winsize *slave_winsize)
383 {
384     int         fdm, fds, streams_pty;
385     pid_t       pid;
386     char        pts_name[20];
387
388     if (!forkpty_ok)
389         fatal(0, "Protocol not yet supported, use telnet", 0);
390
391     if ( (fdm = ptym_open(pts_name, sizeof(pts_name), &streams_pty)) < 0)
392         return -1;
393
394     if (slave_name != NULL)
395         /* Return name of slave */
396         strlcpy(slave_name, pts_name, slave_name_sz);
397
398     pid = fork();
399     if (pid < 0)
400         return(-1);
401     else if (pid == 0) {                /* Child */
402         if (setsid() < 0)
403             fatal(0, "setsid() failure", errno);
404
405         revoke(slave_name);
406
407 #if defined(NeXT) || defined(ultrix)
408         /* The NeXT is severely broken, this makes things slightly
409          * better but we still doesn't get a working pty. If there
410          * where a TIOCSCTTY we could perhaps fix things but... The
411          * same problem also exists in xterm! */
412         if (setpgrp(0, 0) < 0)
413             fatal(0, "NeXT kludge failed setpgrp", errno);
414 #endif
415
416         /* SVR4 acquires controlling terminal on open() */
417         if ( (fds = ptys_open(fdm, pts_name, streams_pty)) < 0)
418             return -1;
419         close(fdm);             /* All done with master in child */
420         
421 #if     defined(TIOCSCTTY) && !defined(CIBAUD) && !defined(__hpux)
422         /* 44BSD way to acquire controlling terminal */
423         /* !CIBAUD to avoid doing this under SunOS */
424         if (ioctl(fds, TIOCSCTTY, (char *) 0) < 0)
425             return -1;
426 #endif
427 #if defined(NeXT)
428         {
429             int t = open("/dev/tty", O_RDWR);
430             if (t < 0)
431                 fatal(0, "Failed to open /dev/tty", errno);
432             close(fds);
433             fds = t;
434         }
435 #endif
436         /* Set slave's termios and window size */
437         if (slave_termios != NULL) {
438             if (tcsetattr(fds, TCSANOW, slave_termios) < 0)
439                 return -1;
440         }
441 #ifdef TIOCSWINSZ
442         if (slave_winsize != NULL) {
443             if (ioctl(fds, TIOCSWINSZ, slave_winsize) < 0)
444                 return -1;
445         }
446 #endif
447         /* slave becomes stdin/stdout/stderr of child */
448         if (dup2(fds, STDIN_FILENO) != STDIN_FILENO)
449             return -1;
450         if (dup2(fds, STDOUT_FILENO) != STDOUT_FILENO)
451             return -1;
452         if (dup2(fds, STDERR_FILENO) != STDERR_FILENO)
453             return -1;
454         if (fds > STDERR_FILENO)
455             close(fds);
456         return(0);              /* child returns 0 just like fork() */
457     }
458     else {                      /* Parent */
459         *ptrfdm = fdm;  /* Return fd of master */
460         return(pid);    /* Parent returns pid of child */
461     }
462 }
463
464 int
465 forkpty(int *ptrfdm,
466         char *slave_name,
467         struct termios *slave_termios,
468         struct winsize *slave_winsize)
469 {
470     return forkpty_truncate (ptrfdm,
471                              slave_name,
472                              MaxPathLen,
473                              slave_termios,
474                              slave_winsize);
475 }
476
477 #endif /* HAVE_FORKPTY */