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