Fix whitespace throughout.
[fwd] / file.c
CommitLineData
48bb1f76 1/* -*-c-*-
2 *
48bb1f76 3 * File source and target
4 *
5 * (c) 1999 Straylight/Edgeware
6 */
7
206212ca 8/*----- Licensing notice --------------------------------------------------*
48bb1f76 9 *
10 * This file is part of the `fw' port forwarder.
11 *
12 * `fw' is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
206212ca 16 *
48bb1f76 17 * `fw' is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
206212ca 21 *
48bb1f76 22 * You should have received a copy of the GNU General Public License
23 * along with `fw'; if not, write to the Free Software Foundation,
24 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 */
26
48bb1f76 27/*----- Header files ------------------------------------------------------*/
28
29#include "config.h"
30
31#include <ctype.h>
32#include <errno.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36
37#include <sys/types.h>
38#include <sys/stat.h>
39#include <unistd.h>
40#include <fcntl.h>
41
42#include <mLib/alloc.h>
43#include <mLib/dstr.h>
44#include <mLib/fdflags.h>
45#include <mLib/sub.h>
46
47#include "conf.h"
48#include "endpt.h"
49#include "fattr.h"
50#include "file.h"
51#include "fw.h"
52#include "reffd.h"
53#include "scan.h"
54#include "source.h"
55#include "target.h"
56
57/*----- Data structures ---------------------------------------------------*/
58
59/* --- File specification --- */
60
61typedef struct fspec {
62 unsigned type;
63 union {
64 reffd *fd;
65 char *name;
66 } u;
67} fspec;
68
69#define FTYPE_GUESS 0u
70#define FTYPE_FD 1u
71#define FTYPE_NAME 2u
72#define FTYPE_NULL 3u
73
74/* --- File options --- */
75
76typedef struct fopts {
77 unsigned of;
78} fopts;
79
80/* --- File data block --- */
81
82typedef struct fdata {
83 fspec in, out;
84 fattr fa;
85 fopts fo;
86} fdata;
87
88/* --- File source block --- */
89
90typedef struct fsource {
91 source s;
92 fdata f;
206212ca 93} fsource;
48bb1f76 94
95/* --- File target block --- */
96
97typedef struct ftarget {
98 target t;
99 fdata f;
100} ftarget;
101
102/*----- Static variables --------------------------------------------------*/
103
104static fopts file_opts = { O_TRUNC };
105
106static reffd *rstdin, *rstdout;
107static reffd *rnull;
108
109/*----- File endpoint operations ------------------------------------------*/
110
111/* --- @wclose@ --- */
112
113static void fept_wclose(endpt *e)
114{
115 if (e->out) {
116 REFFD_DEC(e->out);
117 e->out = 0;
118 }
119}
120
121/* --- @close@ --- */
122
123static void fept_close(endpt *e)
124{
125 if (e->in)
126 REFFD_DEC(e->in);
127 if (e->out)
128 REFFD_DEC(e->out);
129 fw_dec();
130 DESTROY(e);
131}
132
133/* --- Endpoint operations table --- */
134
268f6af4 135static endpt_ops fept_ops = { 0, 0, fept_wclose, fept_close };
48bb1f76 136
137/*----- General operations on sources and targets -------------------------*/
138
139/* --- @file_null@ --- *
140 *
141 * Arguments: @void *p@ = an uninteresting argument
142 *
143 * Returns: ---
144 *
145 * Use: Removes the reference to @rnull@ when the file closes.
146 */
147
148static void file_null(void *p)
149{
150 rnull = 0;
151}
152
153/* --- @file_nullref@ --- *
154 *
155 * Arguments: ---
156 *
157 * Returns: A reference to a file descriptor open on /dev/null.
158 *
159 * Use: Obtains a file descriptor for /dev/null. The reference
160 * @rnull@ is `weak', so that I only have a descriptor open when
161 * I actually need one.
162 */
163
164static reffd *file_nullref(void)
165{
166 if (rnull)
167 REFFD_INC(rnull);
168 else {
169 int n;
170 if ((n = open("/dev/null", O_RDWR)) < 0) {
171 fw_log(-1, "couldn't open `/dev/null': %s", strerror(errno));
172 return (0);
173 }
174 fdflags(n, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
175 rnull = reffd_init(n);
176 reffd_handler(rnull, file_null, 0);
177 }
178 return (rnull);
206212ca 179}
48bb1f76 180
181/* --- @file_fspec@ --- *
182 *
183 * Arguments: @fspec *f@ = pointer to filespec to fill in
184 * @scanner *sc@ = pointer to scanner to read from
185 *
186 * Returns: ---
187 *
188 * Use: Reads in a file spec.
189 */
190
191static void file_fspec(fspec *f, scanner *sc)
192{
193 int c = 0;
194 unsigned type = FTYPE_GUESS;
195 reffd *fd = 0;
196
197 /* --- Set up references to @stdin@ and @stdout@ --- */
198
199 if (!rstdin) {
200 fdflags(STDIN_FILENO, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
201 fdflags(STDOUT_FILENO, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
202 rstdin = reffd_init(STDIN_FILENO);
203 rstdout = reffd_init(STDOUT_FILENO);
204 }
205
206 /* --- Try to get an access type --- */
207
208 if (sc->t == ':') {
209 c = 1;
210 token(sc);
211 }
212
213 if (sc->t == CTOK_WORD) {
214 int i = conf_enum(sc, "fd,name,null", c ? ENUM_ABBREV : ENUM_NONE,
215 "file access type");
216 type = i + 1;
217 if (type && sc->t == ':')
218 token(sc);
219 }
220
221 /* --- Check for a valid file descriptor name --- */
222
223 if (sc->t == CTOK_WORD && type != FTYPE_NAME && type != FTYPE_NULL) {
224 if (strcmp(sc->d.buf, "stdin") == 0) {
225 fd = rstdin;
226 REFFD_INC(rstdin);
227 } else if (strcmp(sc->d.buf, "stdout") == 0) {
228 fd = rstdout;
229 REFFD_INC(rstdout);
230 }
231 if (fd)
232 REFFD_INC(fd);
233 else if (isdigit((unsigned char)*sc->d.buf)) {
234 int nfd = atoi(sc->d.buf);
235 switch (nfd) {
236 case STDIN_FILENO: fd = rstdin; REFFD_INC(rstdin); break;
237 case STDOUT_FILENO: fd = rstdout; REFFD_INC(rstdout); break;
238 default:
239 fd = reffd_init(nfd);
240 fdflags(nfd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
241 break;
242 }
243 }
244 }
245
246 /* --- Sort out what to do as a result --- */
247
248 if (type == FTYPE_NULL)
249 f->type = FTYPE_NULL;
250 else if (fd) {
251 f->type = FTYPE_FD;
252 f->u.fd = fd;
206212ca 253 token(sc);
48bb1f76 254 } else if (type == FTYPE_FD) {
255 if (sc->t == CTOK_WORD)
256 error(sc, "bad file descriptor `%s'", sc->d.buf);
257 else
258 error(sc, "parse error, expected file descriptor");
259 } else {
260 dstr d = DSTR_INIT;
261 conf_name(sc, '/', &d);
262 f->type = FTYPE_NAME;
263 f->u.name = xstrdup(d.buf);
264 dstr_destroy(&d);
265 }
266}
267
268/* --- @read@ --- */
269
270static void file_read(fdata *f, scanner *sc)
271{
272 file_fspec(&f->in, sc);
273 if (sc->t != ',') {
274 f->out = f->in;
275 if (f->out.type == FTYPE_FD && f->out.u.fd == rstdin)
276 f->out.u.fd = rstdout;
277 } else {
278 token(sc);
279 file_fspec(&f->out, sc);
280 }
281 f->fa = fattr_global;
282 f->fo = file_opts;
283}
284
285/* --- @print@ --- */
286
287static void file_pfspec(fspec *f, dstr *d)
288{
289 switch (f->type) {
290 case FTYPE_FD:
291 switch (f->u.fd->fd) {
292 case STDIN_FILENO:
293 dstr_puts(d, "stdin");
294 break;
295 case STDOUT_FILENO:
296 dstr_puts(d, "stdout");
297 break;
298 default:
299 dstr_putf(d, "fd:%i", f->u.fd->fd);
300 break;
301 }
302 break;
303 case FTYPE_NAME:
304 dstr_putf(d, "name:%s", f->u.name);
305 break;
306 case FTYPE_NULL:
307 dstr_puts(d, "null");
308 break;
206212ca 309 }
48bb1f76 310}
311
312static void file_desc(fdata *f, dstr *d)
313{
314 dstr_puts(d, "file ");
315 file_pfspec(&f->in, d);
316 dstr_puts(d, ", ");
317 file_pfspec(&f->out, d);
318}
319
320/* --- @option@ --- */
321
322static int file_option(fdata *f, scanner *sc)
323{
324 fopts *fo = f ? &f->fo : &file_opts;
325
326 CONF_BEGIN(sc, "file", "file")
327
328 /* --- Handle file-specific options --- */
329
330 if (strcmp(sc->d.buf, "create") == 0) {
331 token(sc);
332 if (sc->t == '=')
333 token(sc);
334 switch (conf_enum(sc, "no,yes", ENUM_ABBREV, "create status")) {
335 case 0: fo->of &= ~O_CREAT; break;
336 case 1: fo->of |= O_CREAT; break;
337 }
338 CONF_ACCEPT;
339 }
340
341 else if (strcmp(sc->d.buf, "open") == 0) {
342 token(sc);
343 if (sc->t == '=')
344 token(sc);
345 fo->of &= ~(O_TRUNC | O_APPEND | O_EXCL);
346 switch (conf_enum(sc, "no,truncate,append",
347 ENUM_ABBREV, "open status")) {
348 case 0: fo->of |= O_EXCL; break;
349 case 1: fo->of |= O_TRUNC; break;
350 case 2: fo->of |= O_APPEND; break;
351 }
352 CONF_ACCEPT;
353 }
354
355 /* --- See if it's a file attribute --- */
356
357 {
358 fattr *fa = f ? &f->fa : &fattr_global;
359 if (fattr_option(sc, fa))
360 CONF_ACCEPT;
361 }
362
363 /* --- Nobody understood it --- */
364
365 CONF_END;
366}
367
368/* --- @endpt@ --- */
369
370static endpt *file_endpt(fdata *f, const char *desc)
371{
372 reffd *in = 0, *out = 0;
373 endpt *e;
374
375 /* --- Make the input file first --- */
376
377 switch (f->in.type) {
378 case FTYPE_NULL:
379 in = file_nullref();
380 break;
381 case FTYPE_FD:
382 in = f->in.u.fd;
383 REFFD_INC(in);
384 break;
385 case FTYPE_NAME: {
386 int fd;
387 if ((fd = open(f->in.u.name, O_RDONLY | O_NONBLOCK)) < 0) {
388 fw_log(-1, "[%s] couldn't open `%s' for reading: %s",
389 desc, f->in.u.name, strerror(errno));
390 return (0);
391 }
392 in = reffd_init(fd);
393 } break;
394 }
395
396 /* --- Make the output file second --- */
397
398 switch (f->out.type) {
399 case FTYPE_NULL:
400 out = file_nullref();
401 break;
402 case FTYPE_FD:
403 out = f->out.u.fd;
404 REFFD_INC(out);
405 break;
406 case FTYPE_NAME: {
407 int m = f->fo.of | O_WRONLY | O_NONBLOCK;
408 int fd = -1;
409
410 /* --- First try creating the file --- *
411 *
412 * This lets me know whether to set the file attributes or not. It
413 * also stands a chance of noticing people playing silly beggars with
414 * dangling symlinks.
415 */
416
417 if ((m & O_CREAT) &&
418 (fd = open(f->out.u.name, m | O_EXCL, f->fa.mode)) < 0 &&
419 (errno != EEXIST || (m & O_EXCL))) {
420 fw_log(-1, "[%s] couldn't create `%s': %s",
421 desc, f->out.u.name, strerror(errno));
422 REFFD_DEC(in);
423 return (0);
424 }
425
426 if (fd != -1) {
427 if (fattr_apply(f->out.u.name, &f->fa)) {
59571fdf 428 fw_log(-1, "[%s] couldn't apply file attributes to `%s': %s",
48bb1f76 429 desc, f->out.u.name, strerror(errno));
430 }
431 } else if ((fd = open(f->out.u.name, m & ~O_CREAT)) < 0) {
432 fw_log(-1, "[%s] couldn't open `%s': %s",
433 desc, f->out.u.name, strerror(errno));
434 REFFD_DEC(in);
435 return (0);
436 }
437
438 fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
439 out = reffd_init(fd);
440 } break;
441 }
442
443 /* --- Set up the endpoint --- */
444
445 e = CREATE(endpt);
446 e->ops = &fept_ops;
447 e->other = 0;
448 e->f = EPF_FILE;
449 e->t = 0;
450 e->in = in;
451 e->out = out;
452 fw_inc();
453 return (e);
454}
455
456/* --- @destroy@ --- */
457
458static void file_destroy(fdata *f)
459{
460 if (f->in.type == FTYPE_NAME)
b0805b27 461 xfree(f->in.u.name);
48bb1f76 462 if (f->out.type == FTYPE_NAME)
b0805b27 463 xfree(f->out.u.name);
48bb1f76 464}
465
466/*----- File source description -------------------------------------------*/
467
468/* --- @option@ --- */
469
470static int fsource_option(source *s, scanner *sc)
471{
472 fsource *fs = (fsource *)s;
473 return (file_option(fs ? &fs->f : 0, sc));
474}
475
476/* --- @read@ --- */
477
478static source *fsource_read(scanner *sc)
479{
480 fsource *fs;
481
482 if (!conf_prefix(sc, "file"))
483 return (0);
484 fs = CREATE(fsource);
485 fs->s.ops = &fsource_ops;
486 fs->s.desc = 0;
487 file_read(&fs->f, sc);
488 return (&fs->s);
489}
490
491/* --- @attach@ --- */
492
493static void fsource_destroy(source */*s*/);
494
495static void fsource_attach(source *s, scanner *sc, target *t)
496{
497 fsource *fs = (fsource *)s;
498 endpt *e, *ee;
499
500 /* --- Set up the source description string --- */
501
502 {
503 dstr d = DSTR_INIT;
504 file_desc(&fs->f, &d);
505 dstr_puts(&d, " -> ");
506 dstr_puts(&d, t->desc);
507 fs->s.desc = xstrdup(d.buf);
508 dstr_destroy(&d);
509 }
510
511 /* --- And then create the endpoints --- */
512
513 if ((ee = t->ops->create(t, fs->s.desc)) == 0)
514 goto tidy;
515 if ((e = file_endpt(&fs->f, fs->s.desc)) == 0) {
516 ee->ops->close(ee);
517 goto tidy;
518 }
519 endpt_join(e, ee);
520
521 /* --- Dispose of the source and target now --- */
522
523tidy:
524 t->ops->destroy(t);
525 fsource_destroy(&fs->s);
526}
527
528/* --- @destroy@ --- */
529
530static void fsource_destroy(source *s)
531{
532 fsource *fs = (fsource *)s;
b0805b27 533 xfree(fs->s.desc);
48bb1f76 534 file_destroy(&fs->f);
535 DESTROY(fs);
536}
537
538/* --- File source operations --- */
539
540source_ops fsource_ops = {
541 "file",
542 fsource_option, fsource_read, fsource_attach, fsource_destroy
543};
544
545/*----- File target description -------------------------------------------*/
546
547/* --- @option@ --- */
548
549static int ftarget_option(target *t, scanner *sc)
550{
551 ftarget *ft = (ftarget *)t;
552 return (file_option(ft ? &ft->f : 0, sc));
553}
554
555/* --- @read@ --- */
556
557static target *ftarget_read(scanner *sc)
558{
559 ftarget *ft;
560 dstr d = DSTR_INIT;
561
562 if (!conf_prefix(sc, "file"))
563 return (0);
564 ft = CREATE(ftarget);
565 ft->t.ops = &ftarget_ops;
566 file_read(&ft->f, sc);
567 file_desc(&ft->f, &d);
568 ft->t.desc = xstrdup(d.buf);
569 dstr_destroy(&d);
570 return (&ft->t);
571}
572
573/* --- @create@ --- */
574
575static endpt *ftarget_create(target *t, const char *desc)
576{
577 ftarget *ft = (ftarget *)t;
578 endpt *e = file_endpt(&ft->f, desc);
579 return (e);
580}
581
582/* --- @destroy@ --- */
583
584static void ftarget_destroy(target *t)
585{
586 ftarget *ft = (ftarget *)t;
587 file_destroy(&ft->f);
b0805b27 588 xfree(ft->t.desc);
48bb1f76 589 DESTROY(ft);
590}
591
592/* --- File target operations --- */
593
594target_ops ftarget_ops = {
595 "file",
ee599f55 596 ftarget_option, ftarget_read, 0, ftarget_create, ftarget_destroy
48bb1f76 597};
598
599/*----- That's all, folks -------------------------------------------------*/