1: #include "global.h"
2:
3:
4: extern char **environ;
5:
6: pio_pid_t pio_pidlist = SLIST_HEAD_INITIALIZER(pio_pidlist);
7: static pthread_mutex_t pidlist_mutex = PTHREAD_MUTEX_INITIALIZER;
8:
9: #define THREAD_LOCK() if (__isthreaded) pthread_mutex_lock(&pidlist_mutex)
10: #define THREAD_UNLOCK() if (__isthreaded) pthread_mutex_unlock(&pidlist_mutex)
11:
12:
13: /*
14: * e_popen() - ELWIX replacement of standard popen
15: *
16: * @command = command
17: * @type = type
18: * @ppid = return pid of child program
19: * return: NULL error or !=NULL open program
20: */
21: FILE *
22: e_popen(const char *command, const char *type, pid_t *ppid)
23: {
24: struct tagPIOPID *cur, *p;
25: FILE *iop;
26: int pdes[2], pid, twoway, cloexec;
27: char *argv[4];
28:
29: if (!command || !type)
30: return NULL;
31:
32: cloexec = strchr(type, 'e') != NULL;
33: /*
34: * Lite2 introduced two-way popen() pipes using _socketpair().
35: * FreeBSD's pipe() is bidirectional, so we use that.
36: */
37: if (strchr(type, '+')) {
38: twoway = 1;
39: type = "r+";
40: } else {
41: twoway = 0;
42: if ((*type != 'r' && *type != 'w') ||
43: (type[1] && (type[1] != 'e' || type[2])))
44: return (NULL);
45: }
46: if ((cloexec ? pipe2(pdes, O_CLOEXEC) : pipe(pdes)) < 0)
47: return (NULL);
48:
49: if (!(cur = e_malloc(sizeof(struct tagPIOPID)))) {
50: close(pdes[0]);
51: close(pdes[1]);
52: return (NULL);
53: }
54:
55: argv[0] = "sh";
56: argv[1] = "-c";
57: argv[2] = (char *)command;
58: argv[3] = NULL;
59:
60: THREAD_LOCK();
61: switch (pid = vfork()) {
62: case -1: /* Error. */
63: THREAD_UNLOCK();
64: close(pdes[0]);
65: close(pdes[1]);
66: e_free(cur);
67: return (NULL);
68: /* NOTREACHED */
69: case 0: /* Child. */
70: if (*type == 'r') {
71: /*
72: * The _dup2() to STDIN_FILENO is repeated to avoid
73: * writing to pdes[1], which might corrupt the
74: * parent's copy. This isn't good enough in
75: * general, since the _exit() is no return, so
76: * the compiler is free to corrupt all the local
77: * variables.
78: */
79: if (!cloexec)
80: close(pdes[0]);
81: if (pdes[1] != STDOUT_FILENO) {
82: dup2(pdes[1], STDOUT_FILENO);
83: if (!cloexec)
84: close(pdes[1]);
85: if (twoway)
86: dup2(STDOUT_FILENO, STDIN_FILENO);
87: } else if (twoway && (pdes[1] != STDIN_FILENO)) {
88: dup2(pdes[1], STDIN_FILENO);
89: if (cloexec)
90: fcntl(pdes[1], F_SETFD, 0);
91: } else if (cloexec)
92: fcntl(pdes[1], F_SETFD, 0);
93: } else {
94: if (pdes[0] != STDIN_FILENO) {
95: dup2(pdes[0], STDIN_FILENO);
96: if (!cloexec)
97: close(pdes[0]);
98: } else if (cloexec)
99: fcntl(pdes[0], F_SETFD, 0);
100: if (!cloexec)
101: close(pdes[1]);
102: }
103: SLIST_FOREACH(p, &pio_pidlist, next)
104: close(fileno(p->fp));
105: execve(_PATH_BSHELL, argv, environ);
106: _exit(127);
107: /* NOTREACHED */
108: default:
109: if (ppid)
110: *ppid = pid;
111: }
112: THREAD_UNLOCK();
113:
114: /* Parent; assume fdopen can't fail. */
115: if (*type == 'r') {
116: iop = fdopen(pdes[0], type);
117: close(pdes[1]);
118: } else {
119: iop = fdopen(pdes[1], type);
120: close(pdes[0]);
121: }
122:
123: /* Link into list of file descriptors. */
124: cur->fp = iop;
125: cur->pid = pid;
126: THREAD_LOCK();
127: SLIST_INSERT_HEAD(&pio_pidlist, cur, next);
128: THREAD_UNLOCK();
129:
130: return (iop);
131: }
132:
133: /*
134: * e_pclose() - ELWIX replacement of standard pclose
135: *
136: * @iop = popen handle
137: * return: -1 error or !=-1 pid status
138: */
139: int
140: e_pclose(FILE *iop)
141: {
142: struct tagPIOPID *cur, *last = NULL;
143: int pstat;
144: pid_t pid;
145:
146: if (!iop)
147: return -1;
148:
149: /*
150: * Find the appropriate file pointer and remove it from the list.
151: */
152: THREAD_LOCK();
153: SLIST_FOREACH(cur, &pio_pidlist, next) {
154: if (cur->fp == iop)
155: break;
156: last = cur;
157: }
158: if (!cur) {
159: THREAD_UNLOCK();
160: return (-1);
161: }
162: if (!last)
163: SLIST_REMOVE_HEAD(&pio_pidlist, next);
164: else
165: SLIST_REMOVE_AFTER(last, next);
166: THREAD_UNLOCK();
167:
168: fclose(iop);
169:
170: do {
171: pid = wait4(cur->pid, &pstat, 0, NULL);
172: } while (pid == -1 && errno == EINTR);
173:
174: e_free(cur);
175:
176: return (pid == -1 ? -1 : pstat);
177: }
178:
179: /*
180: * pio_pgetpid() - Get tagPIOPID structure from file handle
181: *
182: * @iop = popen handle
183: * return: NULL error or !=NULL tagPIOPID structure
184: */
185: struct tagPIOPID *
186: pio_pgetpid(FILE * __restrict iop)
187: {
188: struct tagPIOPID *p;
189:
190: if (!iop)
191: return NULL;
192:
193: THREAD_LOCK();
194: SLIST_FOREACH(p, &pio_pidlist, next)
195: if (p->fp == iop)
196: break;
197: THREAD_UNLOCK();
198:
199: return p;
200: }
201:
202: /*
203: * pio_pchkpid() - Check exit status of child programs
204: *
205: * @pids = return tagPIOPID structures of exited programs,
206: * if !=NULL must call array_Destroy()
207: * return: -1 error or >-1 exited programs
208: */
209: int
210: pio_pchkpid(array_t ** __restrict pids)
211: {
212: register int ret = 0;
213: struct tagPIOPID *p;
214: array_t *pa;
215:
216: if (pids) {
217: if (!(pa = array_Init(0)))
218: return -1;
219: else
220: *pids = pa;
221: }
222:
223: THREAD_LOCK();
224: SLIST_FOREACH(p, &pio_pidlist, next)
225: if (p->fp && waitpid(p->pid, &p->stat, WNOHANG) > 0) {
226: if (pids)
227: array_Push(pa, p, 0);
228: ret++;
229: }
230: THREAD_UNLOCK();
231:
232: return ret;
233: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>