OSDN Git Service

drop support for old systems lacking vfork
[uclinux-h8/uClibc.git] / libc / stdlib / unix_grantpt.c
1 /* Copyright (C) 1998 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Zack Weinberg <zack@rabi.phys.columbia.edu>, 1998.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public
16    License along with the GNU C Library; see the file COPYING.LIB.  If not,
17    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    Boston, MA 02111-1307, USA.  */
19
20 #include <assert.h>
21 #include <errno.h>
22 #include <grp.h>
23 #include <limits.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/resource.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <sys/wait.h>
30 #include <unistd.h>
31 #include "pty-private.h"
32
33
34 /* Return the result of ptsname_r in the buffer pointed to by PTS,
35    which should be of length BUF_LEN.  If it is too long to fit in
36    this buffer, a sufficiently long buffer is allocated using malloc,
37    and returned in PTS.  0 is returned upon success, -1 otherwise.  */
38 static int
39 pts_name (int fd, char **pts, size_t buf_len)
40 {
41   int rv;
42   char *buf = *pts;
43
44   for (;;)
45     {
46       char *new_buf;
47
48       if (buf_len)
49         {
50           rv = ptsname_r (fd, buf, buf_len);
51
52           if (rv != 0 || memchr (buf, '\0', buf_len))
53             /* We either got an error, or we succeeded and the
54                returned name fit in the buffer.  */
55             break;
56
57           /* Try again with a longer buffer.  */
58           buf_len += buf_len;   /* Double it */
59         }
60       else
61         /* No initial buffer; start out by mallocing one.  */
62         buf_len = 128;          /* First time guess.  */
63
64       if (buf != *pts)
65         /* We've already malloced another buffer at least once.  */
66         new_buf = realloc (buf, buf_len);
67       else
68         new_buf = malloc (buf_len);
69       if (! new_buf)
70         {
71           rv = -1;
72           errno = ENOMEM;
73           break;
74         }
75       buf = new_buf;
76     }
77
78   if (rv == 0)
79     *pts = buf;         /* Return buffer to the user.  */
80   else if (buf != *pts)
81     free (buf);         /* Free what we malloced when returning an error.  */
82
83   return rv;
84 }
85
86 /* Change the ownership and access permission of the slave pseudo
87    terminal associated with the master pseudo terminal specified
88    by FD.  */
89 int
90 grantpt (int fd)
91 {
92   int retval = -1;
93 #ifdef PATH_MAX
94   char _buf[PATH_MAX];
95 #else
96   char _buf[512];
97 #endif
98   char *buf = _buf;
99   struct stat st;
100   uid_t uid;
101   gid_t gid;
102   pid_t pid;
103
104   if (pts_name (fd, &buf, sizeof (_buf)))
105     return -1;
106
107   if (stat(buf, &st) < 0)
108     goto cleanup;
109
110   /* Make sure that we own the device.  */
111   uid = getuid ();
112   if (st.st_uid != uid)
113     {
114       if (chown (buf, uid, st.st_gid) < 0)
115         goto helper;
116     }
117
118   gid = getgid ();
119
120   /* Make sure the group of the device is that special group.  */
121   if (st.st_gid != gid)
122     {
123       if (chown (buf, uid, gid) < 0)
124         goto helper;
125     }
126
127   /* Make sure the permission mode is set to readable and writable by
128      the owner, and writable by the group.  */
129   if ((st.st_mode & ACCESSPERMS) != (S_IRUSR|S_IWUSR|S_IWGRP))
130     {
131       if (chmod (buf, S_IRUSR|S_IWUSR|S_IWGRP) < 0)
132         goto helper;
133     }
134
135   retval = 0;
136   goto cleanup;
137
138   /* We have to use the helper program.  */
139  helper:
140
141   pid = vfork ();
142   if (pid == -1)
143     goto cleanup;
144   else if (pid == 0)
145     {
146       /* Disable core dumps.  */
147       struct rlimit rl = { 0, 0 };
148       setrlimit (RLIMIT_CORE, &rl);
149
150       /* We pase the master pseudo terminal as file descriptor PTY_FILENO.  */
151       if (fd != PTY_FILENO)
152         if (dup2 (fd, PTY_FILENO) < 0)
153           _exit (FAIL_EBADF);
154
155       execle (_PATH_PT_CHOWN, _PATH_PT_CHOWN, NULL, NULL);
156       _exit (FAIL_EXEC);
157     }
158   else
159     {
160       int w;
161
162       if (waitpid (pid, &w, 0) == -1)
163         goto cleanup;
164       if (!WIFEXITED (w))
165         errno = ENOEXEC;
166       else
167         switch (WEXITSTATUS(w))
168           {
169           case 0:
170             retval = 0;
171             break;
172           case FAIL_EBADF:
173             errno = EBADF;
174             break;
175           case FAIL_EINVAL:
176             errno = EINVAL;
177             break;
178           case FAIL_EACCES:
179             errno = EACCES;
180             break;
181           case FAIL_EXEC:
182             errno = ENOEXEC;
183             break;
184
185           default:
186             assert(! "getpt: internal error: invalid exit code from pt_chown");
187           }
188     }
189
190  cleanup:
191   if (buf != _buf)
192     free (buf);
193
194   return retval;
195 }