3 * File source and target
5 * (c) 1999 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of the `fwd' port forwarder.
12 * `fwd' 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.
17 * `fwd' 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.
22 * You should have received a copy of the GNU General Public License
23 * along with `fwd'; if not, write to the Free Software Foundation,
24 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 /*----- Data structures ---------------------------------------------------*/
31 /* --- File specification --- */
33 typedef struct fspec
{
41 #define FTYPE_GUESS 0u
46 /* --- File options --- */
48 typedef struct fopts
{
52 /* --- File data block --- */
54 typedef struct fdata
{
60 /* --- File source block --- */
62 typedef struct fsource
{
67 /* --- File target block --- */
69 typedef struct ftarget
{
74 /*----- Static variables --------------------------------------------------*/
76 static fopts file_opts
= { O_TRUNC
};
78 static reffd
*rstdin
, *rstdout
;
81 /*----- File endpoint operations ------------------------------------------*/
83 /* --- @wclose@ --- */
85 static void fept_wclose(endpt
*e
)
95 static void fept_close(endpt
*e
)
105 /* --- Endpoint operations table --- */
107 static endpt_ops fept_ops
= { 0, 0, fept_wclose
, fept_close
};
109 /*----- General operations on sources and targets -------------------------*/
111 /* --- @file_null@ --- *
113 * Arguments: @void *p@ = an uninteresting argument
117 * Use: Removes the reference to @rnull@ when the file closes.
120 static void file_null(void *p
)
125 /* --- @file_nullref@ --- *
129 * Returns: A reference to a file descriptor open on /dev/null.
131 * Use: Obtains a file descriptor for /dev/null. The reference
132 * @rnull@ is `weak', so that I only have a descriptor open when
133 * I actually need one.
136 static reffd
*file_nullref(void)
142 if ((n
= open("/dev/null", O_RDWR
)) < 0) {
143 fw_log(NOW
, "couldn't open `/dev/null': %s", strerror(errno
));
146 fdflags(n
, O_NONBLOCK
, O_NONBLOCK
, FD_CLOEXEC
, FD_CLOEXEC
);
147 rnull
= reffd_init(n
);
148 reffd_handler(rnull
, file_null
, 0);
153 /* --- @file_fspec@ --- *
155 * Arguments: @fspec *f@ = pointer to filespec to fill in
156 * @scanner *sc@ = pointer to scanner to read from
160 * Use: Reads in a file spec.
163 static void file_fspec(fspec
*f
, scanner
*sc
)
166 unsigned type
= FTYPE_GUESS
;
169 /* --- Set up references to @stdin@ and @stdout@ --- */
172 fdflags(STDIN_FILENO
, O_NONBLOCK
, O_NONBLOCK
, FD_CLOEXEC
, FD_CLOEXEC
);
173 fdflags(STDOUT_FILENO
, O_NONBLOCK
, O_NONBLOCK
, FD_CLOEXEC
, FD_CLOEXEC
);
174 rstdin
= reffd_init(STDIN_FILENO
);
175 rstdout
= reffd_init(STDOUT_FILENO
);
178 /* --- Try to get an access type --- */
185 if (sc
->t
== CTOK_WORD
) {
186 int i
= conf_enum(sc
, "fd,name,null", c ? ENUM_ABBREV
: ENUM_NONE
,
189 if (type
&& sc
->t
== ':')
193 /* --- Check for a valid file descriptor name --- */
195 if (sc
->t
== CTOK_WORD
&& type
!= FTYPE_NAME
&& type
!= FTYPE_NULL
) {
196 if (strcmp(sc
->d
.buf
, "stdin") == 0) {
199 } else if (strcmp(sc
->d
.buf
, "stdout") == 0) {
205 else if (isdigit((unsigned char)*sc
->d
.buf
)) {
206 int nfd
= atoi(sc
->d
.buf
);
208 case STDIN_FILENO
: fd
= rstdin
; REFFD_INC(rstdin
); break;
209 case STDOUT_FILENO
: fd
= rstdout
; REFFD_INC(rstdout
); break;
211 fd
= reffd_init(nfd
);
212 fdflags(nfd
, O_NONBLOCK
, O_NONBLOCK
, FD_CLOEXEC
, FD_CLOEXEC
);
218 /* --- Sort out what to do as a result --- */
220 if (type
== FTYPE_NULL
)
221 f
->type
= FTYPE_NULL
;
226 } else if (type
== FTYPE_FD
) {
227 if (sc
->t
== CTOK_WORD
)
228 error(sc
, "bad file descriptor `%s'", sc
->d
.buf
);
230 error(sc
, "parse error, expected file descriptor");
234 f
->type
= FTYPE_NAME
;
235 f
->u
.name
= xstrdup(d
.buf
);
242 static void file_read(fdata
*f
, scanner
*sc
)
244 file_fspec(&f
->in
, sc
);
247 file_fspec(&f
->out
, sc
);
250 if (f
->out
.type
== FTYPE_FD
&& f
->out
.u
.fd
== rstdin
)
251 f
->out
.u
.fd
= rstdout
;
252 else if (f
->out
.type
== FTYPE_NAME
)
253 f
->out
.u
.name
= xstrdup(f
->in
.u
.name
);
255 f
->fa
= fattr_global
;
259 /* --- @print@ --- */
261 static void file_pfspec(fspec
*f
, dstr
*d
)
265 switch (f
->u
.fd
->fd
) {
267 dstr_puts(d
, "stdin");
270 dstr_puts(d
, "stdout");
273 dstr_putf(d
, "fd:%i", f
->u
.fd
->fd
);
278 dstr_putf(d
, "name:%s", f
->u
.name
);
281 dstr_puts(d
, "null");
286 static void file_desc(fdata
*f
, dstr
*d
)
288 dstr_puts(d
, "file ");
289 file_pfspec(&f
->in
, d
);
291 file_pfspec(&f
->out
, d
);
294 /* --- @option@ --- */
296 static int file_option(fdata
*f
, scanner
*sc
)
298 fopts
*fo
= f ?
&f
->fo
: &file_opts
;
300 CONF_BEGIN(sc
, "file", "file")
302 /* --- Handle file-specific options --- */
304 if (strcmp(sc
->d
.buf
, "create") == 0) {
308 switch (conf_enum(sc
, "no,yes", ENUM_ABBREV
, "create status")) {
309 case 0: fo
->of
&= ~O_CREAT
; break;
310 case 1: fo
->of
|= O_CREAT
; break;
315 else if (strcmp(sc
->d
.buf
, "open") == 0) {
319 fo
->of
&= ~(O_TRUNC
| O_APPEND
| O_EXCL
);
320 switch (conf_enum(sc
, "no,truncate,append",
321 ENUM_ABBREV
, "open status")) {
322 case 0: fo
->of
|= O_EXCL
; break;
323 case 1: fo
->of
|= O_TRUNC
; break;
324 case 2: fo
->of
|= O_APPEND
; break;
329 /* --- See if it's a file attribute --- */
332 fattr
*fa
= f ?
&f
->fa
: &fattr_global
;
333 if (fattr_option(sc
, fa
))
337 /* --- Nobody understood it --- */
342 /* --- @endpt@ --- */
344 static endpt
*file_endpt(fdata
*f
, const char *desc
)
346 reffd
*in
= 0, *out
= 0;
349 /* --- Make the input file first --- */
351 switch (f
->in
.type
) {
361 if ((fd
= open(f
->in
.u
.name
, O_RDONLY
| O_NONBLOCK
)) < 0) {
362 fw_log(NOW
, "[%s] couldn't open `%s' for reading: %s",
363 desc
, f
->in
.u
.name
, strerror(errno
));
370 /* --- Make the output file second --- */
372 switch (f
->out
.type
) {
374 out
= file_nullref();
381 int m
= f
->fo
.of
| O_WRONLY
| O_NONBLOCK
;
384 /* --- First try creating the file --- *
386 * This lets me know whether to set the file attributes or not. It
387 * also stands a chance of noticing people playing silly beggars with
392 (fd
= open(f
->out
.u
.name
, m
| O_EXCL
, f
->fa
.mode
)) < 0 &&
393 (errno
!= EEXIST
|| (m
& O_EXCL
))) {
394 fw_log(NOW
, "[%s] couldn't create `%s': %s",
395 desc
, f
->out
.u
.name
, strerror(errno
));
401 if (fattr_apply(f
->out
.u
.name
, &f
->fa
)) {
402 fw_log(NOW
, "[%s] couldn't apply file attributes to `%s': %s",
403 desc
, f
->out
.u
.name
, strerror(errno
));
405 } else if ((fd
= open(f
->out
.u
.name
, m
& ~O_CREAT
)) < 0) {
406 fw_log(NOW
, "[%s] couldn't open `%s': %s",
407 desc
, f
->out
.u
.name
, strerror(errno
));
412 fdflags(fd
, O_NONBLOCK
, O_NONBLOCK
, FD_CLOEXEC
, FD_CLOEXEC
);
413 out
= reffd_init(fd
);
417 /* --- Set up the endpoint --- */
430 /* --- @destroy@ --- */
432 static void file_destroy(fdata
*f
)
434 if (f
->in
.type
== FTYPE_NAME
)
436 if (f
->out
.type
== FTYPE_NAME
)
437 xfree(f
->out
.u
.name
);
440 /*----- File source description -------------------------------------------*/
442 /* --- @option@ --- */
444 static int fsource_option(source
*s
, scanner
*sc
)
446 fsource
*fs
= (fsource
*)s
;
447 return (file_option(fs ?
&fs
->f
: 0, sc
));
452 static source
*fsource_read(scanner
*sc
)
456 if (!conf_prefix(sc
, "file"))
458 fs
= CREATE(fsource
);
459 fs
->s
.ops
= &fsource_ops
;
461 file_read(&fs
->f
, sc
);
465 /* --- @attach@ --- */
467 static void fsource_destroy(source */
*s*/
);
469 static void fsource_attach(source
*s
, scanner
*sc
, target
*t
)
471 fsource
*fs
= (fsource
*)s
;
474 /* --- Set up the source description string --- */
478 file_desc(&fs
->f
, &d
);
479 dstr_puts(&d
, " -> ");
480 dstr_puts(&d
, t
->desc
);
481 fs
->s
.desc
= xstrdup(d
.buf
);
485 /* --- And then create the endpoints --- */
487 if ((ee
= t
->ops
->create(t
, fs
->s
.desc
)) == 0)
489 if ((e
= file_endpt(&fs
->f
, fs
->s
.desc
)) == 0) {
493 endpt_join(e
, ee
, fs
->s
.desc
);
495 /* --- Dispose of the source and target now --- */
499 fsource_destroy(&fs
->s
);
502 /* --- @destroy@ --- */
504 static void fsource_destroy(source
*s
)
506 fsource
*fs
= (fsource
*)s
;
508 file_destroy(&fs
->f
);
512 /* --- File source operations --- */
514 source_ops fsource_ops
= {
516 fsource_option
, fsource_read
, fsource_attach
, fsource_destroy
519 /*----- File target description -------------------------------------------*/
521 /* --- @option@ --- */
523 static int ftarget_option(target
*t
, scanner
*sc
)
525 ftarget
*ft
= (ftarget
*)t
;
526 return (file_option(ft ?
&ft
->f
: 0, sc
));
531 static target
*ftarget_read(scanner
*sc
)
536 if (!conf_prefix(sc
, "file"))
538 ft
= CREATE(ftarget
);
539 ft
->t
.ops
= &ftarget_ops
;
540 file_read(&ft
->f
, sc
);
541 file_desc(&ft
->f
, &d
);
542 ft
->t
.desc
= xstrdup(d
.buf
);
547 /* --- @create@ --- */
549 static endpt
*ftarget_create(target
*t
, const char *desc
)
551 ftarget
*ft
= (ftarget
*)t
;
552 endpt
*e
= file_endpt(&ft
->f
, desc
);
556 /* --- @destroy@ --- */
558 static void ftarget_destroy(target
*t
)
560 ftarget
*ft
= (ftarget
*)t
;
561 file_destroy(&ft
->f
);
566 /* --- File target operations --- */
568 target_ops ftarget_ops
= {
570 ftarget_option
, ftarget_read
, 0, ftarget_create
, ftarget_destroy
573 /*----- That's all, folks -------------------------------------------------*/