Annotation of libelwix/src/pio.c, revision 1.1.2.4
1.1.2.1 misho 1: #include "global.h"
2:
3:
4: extern char **environ;
5:
1.1.2.2 misho 6: pio_pid_t pio_pidlist = SLIST_HEAD_INITIALIZER(pio_pidlist);
1.1.2.1 misho 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: {
1.1.2.2 misho 24: struct tagPIOPID *cur, *p;
1.1.2.1 misho 25: FILE *iop;
26: int pdes[2], pid, twoway, cloexec;
27: char *argv[4];
28:
1.1.2.3 misho 29: if (!command || !type)
30: return NULL;
31:
1.1.2.1 misho 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:
1.1.2.2 misho 49: if (!(cur = e_malloc(sizeof(struct tagPIOPID)))) {
1.1.2.1 misho 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: {
1.1.2.2 misho 142: struct tagPIOPID *cur, *last = NULL;
1.1.2.1 misho 143: int pstat;
144: pid_t pid;
145:
1.1.2.3 misho 146: if (!iop)
147: return -1;
148:
1.1.2.1 misho 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: }
1.1.2.2 misho 158: if (!cur) {
1.1.2.1 misho 159: THREAD_UNLOCK();
160: return (-1);
161: }
1.1.2.2 misho 162: if (!last)
1.1.2.1 misho 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 {
1.1.2.2 misho 171: pid = wait4(cur->pid, &pstat, 0, NULL);
1.1.2.1 misho 172: } while (pid == -1 && errno == EINTR);
173:
174: e_free(cur);
175:
176: return (pid == -1 ? -1 : pstat);
177: }
1.1.2.3 misho 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: }
1.1.2.4 ! misho 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>