Contents of /trunk/mkinitrd-magellan/klibc/usr/dash/redir.c
Parent Directory | Revision Log
Revision 1122 -
(show annotations)
(download)
Wed Aug 18 21:11:40 2010 UTC (13 years, 8 months ago) by niro
File MIME type: text/plain
File size: 10443 byte(s)
Wed Aug 18 21:11:40 2010 UTC (13 years, 8 months ago) by niro
File MIME type: text/plain
File size: 10443 byte(s)
-updated to klibc-1.5.19
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, 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, 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 | char *p; |
299 | int pip[2]; |
300 | size_t len = 0; |
301 | |
302 | if (pipe(pip) < 0) |
303 | sh_error("Pipe call failed"); |
304 | |
305 | p = redir->nhere.doc->narg.text; |
306 | if (redir->type == NXHERE) { |
307 | expandarg(redir->nhere.doc, NULL, EXP_QUOTED); |
308 | p = stackblock(); |
309 | } |
310 | |
311 | len = strlen(p); |
312 | if (len <= PIPESIZE) { |
313 | xwrite(pip[1], p, len); |
314 | goto out; |
315 | } |
316 | |
317 | if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) { |
318 | close(pip[0]); |
319 | signal(SIGINT, SIG_IGN); |
320 | signal(SIGQUIT, SIG_IGN); |
321 | signal(SIGHUP, SIG_IGN); |
322 | #ifdef SIGTSTP |
323 | signal(SIGTSTP, SIG_IGN); |
324 | #endif |
325 | signal(SIGPIPE, SIG_DFL); |
326 | xwrite(pip[1], p, len); |
327 | _exit(0); |
328 | } |
329 | out: |
330 | close(pip[1]); |
331 | return pip[0]; |
332 | } |
333 | |
334 | |
335 | |
336 | /* |
337 | * Undo the effects of the last redirection. |
338 | */ |
339 | |
340 | void |
341 | popredir(int drop) |
342 | { |
343 | struct redirtab *rp; |
344 | int i; |
345 | |
346 | if (--nullredirs >= 0) |
347 | return; |
348 | INTOFF; |
349 | rp = redirlist; |
350 | for (i = 0 ; i < 10 ; i++) { |
351 | switch (rp->renamed[i]) { |
352 | case CLOSED: |
353 | if (!drop) |
354 | close(i); |
355 | break; |
356 | case EMPTY: |
357 | case REALLY_CLOSED: |
358 | break; |
359 | default: |
360 | if (!drop) |
361 | dup2(rp->renamed[i], i); |
362 | close(rp->renamed[i]); |
363 | break; |
364 | } |
365 | } |
366 | redirlist = rp->next; |
367 | nullredirs = rp->nullredirs; |
368 | ckfree(rp); |
369 | INTON; |
370 | } |
371 | |
372 | /* |
373 | * Undo all redirections. Called on error or interrupt. |
374 | */ |
375 | |
376 | #ifdef mkinit |
377 | |
378 | INCLUDE "redir.h" |
379 | |
380 | RESET { |
381 | /* |
382 | * Discard all saved file descriptors. |
383 | */ |
384 | for (;;) { |
385 | nullredirs = 0; |
386 | if (!redirlist) |
387 | break; |
388 | popredir(0); |
389 | } |
390 | } |
391 | |
392 | #endif |
393 | |
394 | |
395 | |
396 | /* |
397 | * Move a file descriptor to > 10. Invokes sh_error on error unless |
398 | * the original file dscriptor is not open. |
399 | */ |
400 | |
401 | int |
402 | savefd(int from, int ofd) |
403 | { |
404 | int newfd; |
405 | int err; |
406 | |
407 | newfd = fcntl(from, F_DUPFD, 10); |
408 | err = newfd < 0 ? errno : 0; |
409 | if (err != EBADF) { |
410 | close(ofd); |
411 | if (err) |
412 | sh_error("%d: %s", from, strerror(err)); |
413 | else |
414 | fcntl(newfd, F_SETFD, FD_CLOEXEC); |
415 | } |
416 | |
417 | return newfd; |
418 | } |
419 | |
420 | |
421 | /* |
422 | * Open a file in noclobber mode. |
423 | * The code was copied from bash. |
424 | */ |
425 | int |
426 | noclobberopen(fname) |
427 | const char *fname; |
428 | { |
429 | int r, fd; |
430 | struct stat64 finfo, finfo2; |
431 | |
432 | /* |
433 | * If the file exists and is a regular file, return an error |
434 | * immediately. |
435 | */ |
436 | r = stat64(fname, &finfo); |
437 | if (r == 0 && S_ISREG(finfo.st_mode)) { |
438 | errno = EEXIST; |
439 | return -1; |
440 | } |
441 | |
442 | /* |
443 | * If the file was not present (r != 0), make sure we open it |
444 | * exclusively so that if it is created before we open it, our open |
445 | * will fail. Make sure that we do not truncate an existing file. |
446 | * Note that we don't turn on O_EXCL unless the stat failed -- if the |
447 | * file was not a regular file, we leave O_EXCL off. |
448 | */ |
449 | if (r != 0) |
450 | return open64(fname, O_WRONLY|O_CREAT|O_EXCL, 0666); |
451 | fd = open64(fname, O_WRONLY|O_CREAT, 0666); |
452 | |
453 | /* If the open failed, return the file descriptor right away. */ |
454 | if (fd < 0) |
455 | return fd; |
456 | |
457 | /* |
458 | * OK, the open succeeded, but the file may have been changed from a |
459 | * non-regular file to a regular file between the stat and the open. |
460 | * We are assuming that the O_EXCL open handles the case where FILENAME |
461 | * did not exist and is symlinked to an existing file between the stat |
462 | * and open. |
463 | */ |
464 | |
465 | /* |
466 | * If we can open it and fstat the file descriptor, and neither check |
467 | * revealed that it was a regular file, and the file has not been |
468 | * replaced, return the file descriptor. |
469 | */ |
470 | if (fstat64(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode) && |
471 | finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino) |
472 | return fd; |
473 | |
474 | /* The file has been replaced. badness. */ |
475 | close(fd); |
476 | errno = EEXIST; |
477 | return -1; |
478 | } |
479 | |
480 | |
481 | int |
482 | redirectsafe(union node *redir, int flags) |
483 | { |
484 | int err; |
485 | volatile int saveint; |
486 | struct jmploc *volatile savehandler = handler; |
487 | struct jmploc jmploc; |
488 | |
489 | SAVEINT(saveint); |
490 | if (!(err = setjmp(jmploc.loc) * 2)) { |
491 | handler = &jmploc; |
492 | redirect(redir, flags); |
493 | } |
494 | handler = savehandler; |
495 | if (err && exception != EXERROR) |
496 | longjmp(handler->loc, 1); |
497 | RESTOREINT(saveint); |
498 | return err; |
499 | } |