Contents of /trunk/mkinitrd-magellan/klibc/usr/dash/redir.c
Parent Directory | Revision Log
Revision 815 -
(show annotations)
(download)
Fri Apr 24 18:32:46 2009 UTC (15 years ago) by niro
File MIME type: text/plain
File size: 10482 byte(s)
Fri Apr 24 18:32:46 2009 UTC (15 years ago) by niro
File MIME type: text/plain
File size: 10482 byte(s)
-updated to klibc-1.5.15
1 | /*- |
2 | * Copyright (c) 1991, 1993 |
3 | * The Regents of the University of California. All rights reserved. |
4 | * Copyright (c) 1997-2005 |
5 | * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to Berkeley by |
8 | * Kenneth Almquist. |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions |
12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. |
18 | * 3. Neither the name of the University nor the names of its contributors |
19 | * may be used to endorse or promote products derived from this software |
20 | * without specific prior written permission. |
21 | * |
22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
32 | * SUCH DAMAGE. |
33 | */ |
34 | |
35 | #include <sys/stat.h> |
36 | #include <sys/types.h> |
37 | #include <sys/param.h> /* PIPE_BUF */ |
38 | #include <signal.h> |
39 | #include <string.h> |
40 | #include <fcntl.h> |
41 | #include <unistd.h> |
42 | #include <stdlib.h> |
43 | |
44 | /* |
45 | * Code for dealing with input/output redirection. |
46 | */ |
47 | |
48 | #include "main.h" |
49 | #include "shell.h" |
50 | #include "nodes.h" |
51 | #include "jobs.h" |
52 | #include "options.h" |
53 | #include "expand.h" |
54 | #include "redir.h" |
55 | #include "output.h" |
56 | #include "memalloc.h" |
57 | #include "error.h" |
58 | |
59 | |
60 | #define REALLY_CLOSED -3 /* fd that was closed and still is */ |
61 | #define EMPTY -2 /* marks an unused slot in redirtab */ |
62 | #define CLOSED -1 /* fd opened for redir needs to be closed */ |
63 | |
64 | #ifndef PIPE_BUF |
65 | # define PIPESIZE 4096 /* amount of buffering in a pipe */ |
66 | #else |
67 | # define PIPESIZE PIPE_BUF |
68 | #endif |
69 | |
70 | |
71 | MKINIT |
72 | struct redirtab { |
73 | struct redirtab *next; |
74 | int renamed[10]; |
75 | int nullredirs; |
76 | }; |
77 | |
78 | |
79 | MKINIT struct redirtab *redirlist; |
80 | MKINIT int nullredirs; |
81 | |
82 | STATIC int openredirect(union node *); |
83 | #ifdef notyet |
84 | STATIC void dupredirect(union node *, int, char[10]); |
85 | #else |
86 | STATIC void dupredirect(union node *, int); |
87 | #endif |
88 | STATIC int openhere(union node *); |
89 | STATIC int noclobberopen(const char *); |
90 | |
91 | |
92 | /* |
93 | * Process a list of redirection commands. If the REDIR_PUSH flag is set, |
94 | * old file descriptors are stashed away so that the redirection can be |
95 | * undone by calling popredir. If the REDIR_BACKQ flag is set, then the |
96 | * standard output, and the standard error if it becomes a duplicate of |
97 | * stdout, is saved in memory. |
98 | */ |
99 | |
100 | void |
101 | redirect(union node *redir, int flags) |
102 | { |
103 | union node *n; |
104 | struct redirtab *sv; |
105 | int i; |
106 | int fd; |
107 | int newfd; |
108 | int *p; |
109 | #if notyet |
110 | char memory[10]; /* file descriptors to write to memory */ |
111 | |
112 | for (i = 10 ; --i >= 0 ; ) |
113 | memory[i] = 0; |
114 | memory[1] = flags & REDIR_BACKQ; |
115 | #endif |
116 | nullredirs++; |
117 | if (!redir) { |
118 | return; |
119 | } |
120 | sv = NULL; |
121 | INTOFF; |
122 | if (likely(flags & REDIR_PUSH)) { |
123 | struct redirtab *q; |
124 | q = ckmalloc(sizeof (struct redirtab)); |
125 | q->next = redirlist; |
126 | redirlist = q; |
127 | q->nullredirs = nullredirs - 1; |
128 | for (i = 0 ; i < 10 ; i++) |
129 | q->renamed[i] = EMPTY; |
130 | nullredirs = 0; |
131 | sv = q; |
132 | } |
133 | n = redir; |
134 | do { |
135 | newfd = openredirect(n); |
136 | if (newfd < -1) |
137 | continue; |
138 | |
139 | fd = n->nfile.fd; |
140 | |
141 | if (sv) { |
142 | p = &sv->renamed[fd]; |
143 | i = *p; |
144 | |
145 | if (likely(i == EMPTY)) { |
146 | i = CLOSED; |
147 | if (fd != newfd) { |
148 | i = savefd(fd); |
149 | fd = -1; |
150 | } |
151 | } |
152 | |
153 | if (i == newfd) |
154 | /* Can only happen if i == newfd == CLOSED */ |
155 | i = REALLY_CLOSED; |
156 | |
157 | *p = i; |
158 | } |
159 | |
160 | if (fd == newfd) |
161 | continue; |
162 | |
163 | #ifdef notyet |
164 | dupredirect(n, newfd, memory); |
165 | #else |
166 | dupredirect(n, newfd); |
167 | #endif |
168 | } while ((n = n->nfile.next)); |
169 | INTON; |
170 | #ifdef notyet |
171 | if (memory[1]) |
172 | out1 = &memout; |
173 | if (memory[2]) |
174 | out2 = &memout; |
175 | #endif |
176 | if (flags & REDIR_SAVEFD2 && sv->renamed[2] >= 0) |
177 | preverrout.fd = sv->renamed[2]; |
178 | } |
179 | |
180 | |
181 | STATIC int |
182 | openredirect(union node *redir) |
183 | { |
184 | char *fname; |
185 | int f; |
186 | |
187 | switch (redir->nfile.type) { |
188 | case NFROM: |
189 | fname = redir->nfile.expfname; |
190 | if ((f = open64(fname, O_RDONLY)) < 0) |
191 | goto eopen; |
192 | break; |
193 | case NFROMTO: |
194 | fname = redir->nfile.expfname; |
195 | if ((f = open64(fname, O_RDWR|O_CREAT|O_TRUNC, 0666)) < 0) |
196 | goto ecreate; |
197 | break; |
198 | case NTO: |
199 | /* Take care of noclobber mode. */ |
200 | if (Cflag) { |
201 | fname = redir->nfile.expfname; |
202 | if ((f = noclobberopen(fname)) < 0) |
203 | goto ecreate; |
204 | break; |
205 | } |
206 | /* FALLTHROUGH */ |
207 | case NCLOBBER: |
208 | fname = redir->nfile.expfname; |
209 | if ((f = open64(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) |
210 | goto ecreate; |
211 | break; |
212 | case NAPPEND: |
213 | fname = redir->nfile.expfname; |
214 | if ((f = open64(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0) |
215 | goto ecreate; |
216 | break; |
217 | case NTOFD: |
218 | case NFROMFD: |
219 | f = redir->ndup.dupfd; |
220 | if (f == redir->nfile.fd) |
221 | f = -2; |
222 | break; |
223 | default: |
224 | #ifdef DEBUG |
225 | abort(); |
226 | #endif |
227 | /* Fall through to eliminate warning. */ |
228 | case NHERE: |
229 | case NXHERE: |
230 | f = openhere(redir); |
231 | break; |
232 | } |
233 | |
234 | return f; |
235 | ecreate: |
236 | sh_error("cannot create %s: %s", fname, errmsg(errno, E_CREAT)); |
237 | eopen: |
238 | sh_error("cannot open %s: %s", fname, errmsg(errno, E_OPEN)); |
239 | } |
240 | |
241 | |
242 | STATIC void |
243 | #ifdef notyet |
244 | dupredirect(redir, f, memory) |
245 | #else |
246 | dupredirect(redir, f) |
247 | #endif |
248 | union node *redir; |
249 | int f; |
250 | #ifdef notyet |
251 | char memory[10]; |
252 | #endif |
253 | { |
254 | int fd = redir->nfile.fd; |
255 | int err = 0; |
256 | |
257 | #ifdef notyet |
258 | memory[fd] = 0; |
259 | #endif |
260 | if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) { |
261 | /* if not ">&-" */ |
262 | if (f >= 0) { |
263 | #ifdef notyet |
264 | if (memory[f]) |
265 | memory[fd] = 1; |
266 | else |
267 | #endif |
268 | if (dup2(f, fd) < 0) { |
269 | err = errno; |
270 | goto err; |
271 | } |
272 | return; |
273 | } |
274 | f = fd; |
275 | } else if (dup2(f, fd) < 0) |
276 | err = errno; |
277 | |
278 | close(f); |
279 | if (err < 0) |
280 | goto err; |
281 | |
282 | return; |
283 | |
284 | err: |
285 | sh_error("%d: %s", f, strerror(err)); |
286 | } |
287 | |
288 | |
289 | /* |
290 | * Handle here documents. Normally we fork off a process to write the |
291 | * data to a pipe. If the document is short, we can stuff the data in |
292 | * the pipe without forking. |
293 | */ |
294 | |
295 | STATIC int |
296 | openhere(union node *redir) |
297 | { |
298 | int pip[2]; |
299 | size_t len = 0; |
300 | |
301 | if (pipe(pip) < 0) |
302 | sh_error("Pipe call failed"); |
303 | if (redir->type == NHERE) { |
304 | len = strlen(redir->nhere.doc->narg.text); |
305 | if (len <= PIPESIZE) { |
306 | xwrite(pip[1], redir->nhere.doc->narg.text, len); |
307 | goto out; |
308 | } |
309 | } |
310 | if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) { |
311 | close(pip[0]); |
312 | signal(SIGINT, SIG_IGN); |
313 | signal(SIGQUIT, SIG_IGN); |
314 | signal(SIGHUP, SIG_IGN); |
315 | #ifdef SIGTSTP |
316 | signal(SIGTSTP, SIG_IGN); |
317 | #endif |
318 | signal(SIGPIPE, SIG_DFL); |
319 | if (redir->type == NHERE) |
320 | xwrite(pip[1], redir->nhere.doc->narg.text, len); |
321 | else |
322 | expandhere(redir->nhere.doc, pip[1]); |
323 | _exit(0); |
324 | } |
325 | out: |
326 | close(pip[1]); |
327 | return pip[0]; |
328 | } |
329 | |
330 | |
331 | |
332 | /* |
333 | * Undo the effects of the last redirection. |
334 | */ |
335 | |
336 | void |
337 | popredir(int drop) |
338 | { |
339 | struct redirtab *rp; |
340 | int i; |
341 | |
342 | if (--nullredirs >= 0) |
343 | return; |
344 | INTOFF; |
345 | rp = redirlist; |
346 | for (i = 0 ; i < 10 ; i++) { |
347 | switch (rp->renamed[i]) { |
348 | case CLOSED: |
349 | if (!drop) |
350 | close(i); |
351 | break; |
352 | case EMPTY: |
353 | case REALLY_CLOSED: |
354 | break; |
355 | default: |
356 | if (!drop) |
357 | dup2(rp->renamed[i], i); |
358 | close(rp->renamed[i]); |
359 | break; |
360 | } |
361 | } |
362 | redirlist = rp->next; |
363 | nullredirs = rp->nullredirs; |
364 | ckfree(rp); |
365 | INTON; |
366 | } |
367 | |
368 | /* |
369 | * Undo all redirections. Called on error or interrupt. |
370 | */ |
371 | |
372 | #ifdef mkinit |
373 | |
374 | INCLUDE "redir.h" |
375 | |
376 | RESET { |
377 | /* |
378 | * Discard all saved file descriptors. |
379 | */ |
380 | for (;;) { |
381 | nullredirs = 0; |
382 | if (!redirlist) |
383 | break; |
384 | popredir(0); |
385 | } |
386 | } |
387 | |
388 | #endif |
389 | |
390 | |
391 | |
392 | /* |
393 | * Move a file descriptor to > 10. Invokes sh_error on error unless |
394 | * the original file dscriptor is not open. |
395 | */ |
396 | |
397 | int |
398 | savefd(int from) |
399 | { |
400 | int newfd; |
401 | int err; |
402 | |
403 | newfd = fcntl(from, F_DUPFD, 10); |
404 | err = newfd < 0 ? errno : 0; |
405 | if (err != EBADF) { |
406 | close(from); |
407 | if (err) |
408 | sh_error("%d: %s", from, strerror(err)); |
409 | else |
410 | fcntl(newfd, F_SETFD, FD_CLOEXEC); |
411 | } |
412 | |
413 | return newfd; |
414 | } |
415 | |
416 | |
417 | /* |
418 | * Open a file in noclobber mode. |
419 | * The code was copied from bash. |
420 | */ |
421 | int |
422 | noclobberopen(fname) |
423 | const char *fname; |
424 | { |
425 | int r, fd; |
426 | struct stat64 finfo, finfo2; |
427 | |
428 | /* |
429 | * If the file exists and is a regular file, return an error |
430 | * immediately. |
431 | */ |
432 | r = stat64(fname, &finfo); |
433 | if (r == 0 && S_ISREG(finfo.st_mode)) { |
434 | errno = EEXIST; |
435 | return -1; |
436 | } |
437 | |
438 | /* |
439 | * If the file was not present (r != 0), make sure we open it |
440 | * exclusively so that if it is created before we open it, our open |
441 | * will fail. Make sure that we do not truncate an existing file. |
442 | * Note that we don't turn on O_EXCL unless the stat failed -- if the |
443 | * file was not a regular file, we leave O_EXCL off. |
444 | */ |
445 | if (r != 0) |
446 | return open64(fname, O_WRONLY|O_CREAT|O_EXCL, 0666); |
447 | fd = open64(fname, O_WRONLY|O_CREAT, 0666); |
448 | |
449 | /* If the open failed, return the file descriptor right away. */ |
450 | if (fd < 0) |
451 | return fd; |
452 | |
453 | /* |
454 | * OK, the open succeeded, but the file may have been changed from a |
455 | * non-regular file to a regular file between the stat and the open. |
456 | * We are assuming that the O_EXCL open handles the case where FILENAME |
457 | * did not exist and is symlinked to an existing file between the stat |
458 | * and open. |
459 | */ |
460 | |
461 | /* |
462 | * If we can open it and fstat the file descriptor, and neither check |
463 | * revealed that it was a regular file, and the file has not been |
464 | * replaced, return the file descriptor. |
465 | */ |
466 | if (fstat64(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode) && |
467 | finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino) |
468 | return fd; |
469 | |
470 | /* The file has been replaced. badness. */ |
471 | close(fd); |
472 | errno = EEXIST; |
473 | return -1; |
474 | } |
475 | |
476 | |
477 | int |
478 | redirectsafe(union node *redir, int flags) |
479 | { |
480 | int err; |
481 | volatile int saveint; |
482 | struct jmploc *volatile savehandler = handler; |
483 | struct jmploc jmploc; |
484 | |
485 | SAVEINT(saveint); |
486 | if (!(err = setjmp(jmploc.loc) * 2)) { |
487 | handler = &jmploc; |
488 | redirect(redir, flags); |
489 | } |
490 | handler = savehandler; |
491 | if (err && exception != EXERROR) |
492 | longjmp(handler->loc, 1); |
493 | RESTOREINT(saveint); |
494 | return err; |
495 | } |