1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2  *@ File- and pipe streams, as well as temporary file creation.
3  *
4  * Copyright (c) 2012 - 2020 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
5  * SPDX-License-Identifier: ISC
6  *
7  * Permission to use, copy, modify, and/or distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 #ifndef mx_FILE_STREAMS_H
20 #define mx_FILE_STREAMS_H
21 
22 #include <mx/nail.h>
23 
24 #define mx_HEADER
25 #include <su/code-in.h>
26 
27 struct mx_fs_tmp_ctx;
28 
29 enum mx_fs_oflags{
30    mx_FS_O_RDONLY = 1u<<0,
31    mx_FS_O_WRONLY = 1u<<1,
32    mx_FS_O_RDWR = 1u<<2,
33    mx_FS_O_APPEND = 1u<<3,
34    mx_FS_O_CREATE = 1u<<4,
35    mx_FS_O_TRUNC = 1u<<5,
36    mx_FS_O_EXCL = 1u<<6,
37    mx_FS_O_UNLINK = 1u<<7, /* Only for tmp_open(): unlink(2) after creation */
38    /* Register file in our file table, causing its close when we jump away
39     * and/or the mainloop ticks otherwise, shall it still exist */
40    mx_FS_O_REGISTER = 1u<<8,
41    /* tmp_open(): unlink at unregistration: O_REGISTER!, !O_UNLINK */
42    mx_FS_O_REGISTER_UNLINK = 1u<<9,
43    /* tmp_open(): do not release signals/unlink: !O_UNLINK! */
44    mx_FS_O_HOLDSIGS = 1u<<10,
45    /* The name hint given to tmp_open() must be a mandatory member of the
46     * result string as a whole.  Also, the random characters are to be placed
47     * before the name hint, not after it */
48    mx_FS_O_SUFFIX = 1u<<11
49 };
50 
51 enum mx_fs_open_state{ /* TODO add mx_fs_open_mode, too */
52    /* Lower bits are in fact enum protocol! */
53    mx_FS_OPEN_STATE_NONE = 0,
54    mx_FS_OPEN_STATE_EXISTS = 1u<<5
55 };
56 MCTA(n_PROTO_MASK < mx_FS_OPEN_STATE_EXISTS, "Bit carrier ranges overlap")
57 
58 /* Note: actually publicly visible part of larger internal struct */
59 struct mx_fs_tmp_ctx{
60    char const *fstc_filename;
61 };
62 
63 /* */
64 #ifdef O_CLOEXEC
65 # define mx_FS_FD_CLOEXEC_SET(FD) (1)
66 #else
67 # define mx_FS_FD_CLOEXEC_SET(FD) mx_fs_fd_cloexec_set(FD)
68 #endif
69 
70 /* oflags implied: cloexec,O_REGISTER.
71  *    {"r", O_RDONLY},
72  *    {"w", O_WRONLY | O_CREAT | n_O_NOXY_BITS | O_TRUNC},
73  *    {"wx", O_WRONLY | O_CREAT | O_EXCL},
74  *    {"a", O_WRONLY | O_APPEND | O_CREAT | mx_O_NOXY_BITS},
75  *    {"a+", O_RDWR | O_APPEND | O_CREAT | mx_O_NOXY_BITS},
76  *    {"r+", O_RDWR},
77  *    {"w+", O_RDWR | O_CREAT | mx_O_NOXY_BITS | O_TRUNC},
78  *    {"W+", O_RDWR | O_CREAT | O_EXCL}
79  * Prepend (!) an ampersand & ("background") to _not_ include O_REGISTER,
80  * in which case the returned file must be closed with normal fclose(3).
81  * mx_O_NOXY_BITS come from mx-config.h */
82 EXPORT FILE *mx_fs_open(char const *file, char const *oflags);
83 
84 /* TODO: Should be Mailbox::create_from_url(URL::from_string(DATA))!
85  * Open file according to oflags (& prefix disallowed)m and register it
86  * (leading ampersand & to suppress this is disallowed).
87  * Handles compressed files, maildir etc.
88  * If fs_or_nil is given it will be filled accordingly */
89 EXPORT FILE *mx_fs_open_any(char const *file, char const *oflags,
90       enum mx_fs_open_state *fs_or_nil);
91 
92 /* Create a temporary file in $TMPDIR, use namehint for its name (prefix
93  * unless O_SUFFIX is set in the fs_oflags oflags and *namehint!=NUL),
94  * and return a stdio FILE pointer with access oflags.
95  * *fstcp_or_nil may only be non-NIL under certain asserted conditions:
96  * - if O_REGISTER: it is fully filled in; whether the filename is actually
97  *   useful depends on the chosen UNLINK mode.
98  * - Else if O_HOLDSIGS: filename filled in, tmp_release() is callable,
99  * - else O_UNLINK must not and O_REGISTER_UNLINK could be set (filename is
100  *   filled in, tmp_release() is not callable.
101  * In the latter two cases autorec memory storage will be created (on success).
102  * One of O_WRONLY and O_RDWR must be set.  Implied: 0600,cloexec */
103 EXPORT FILE *mx_fs_tmp_open(char const *namehint, u32 oflags,
104       struct mx_fs_tmp_ctx **fstcp_or_nil);
105 
106 /* If (O_REGISTER|)O_HOLDSIGS and a context pointer was set when calling
107  * tmp_open(), then sigs_all_*() had not been released yet.
108  * Call this to first unlink(2) the temporary file and then release signals */
109 EXPORT void mx_fs_tmp_release(struct mx_fs_tmp_ctx *fstcp);
110 
111 /* oflags implied: cloexec (unless nocloexec), O_REGISTER */
112 EXPORT FILE *mx_fs_fd_open(sz fd, char const *oflags, boole nocloexec);
113 
114 /* */
115 EXPORT boole mx_fs_fd_cloexec_set(sz fd);
116 
117 /* Close and unregister a FILE* opened with any of fs_open(), fs_open_any(),
118  * fs_tmp_open() (with O_REGISTER) or fd_open() */
119 EXPORT boole mx_fs_close(FILE *fp);
120 
121 /* Create a pair of file descriptors piped together, and ensure the CLOEXEC
122  * bit is set in both; no registration is performed */
123 EXPORT boole mx_fs_pipe_cloexec(sz fd[2]);
124 
125 /* Create a process to be communicated with via a pipe.
126  * mode can be r, W (newfd1 must be set, maybe to CHILD_FD_PASS or
127  * CHILD_FD_NULL) or w (newfd1 is implicitly CHILD_FD_PASS).
128  * In CHILD_FD_PASS cases pipe_close() must be called with waiting enabled,
129  * which is asserted!  Note that child.h is NOT included.
130  * env_addon may be NIL, otherwise it is expected to be a NIL terminated
131  * array of "K=V" strings to be placed into the children's environment
132  * TODO v15 hack: If cmd==(char*)-1 then shell is indeed expected to be a PTF
133  * TODO v15 hack: :P that will be called from within the child process */
134 EXPORT FILE *mx_fs_pipe_open(char const *cmd, char const *mode,
135       char const *shell, char const **env_addon, int newfd1);
136 
137 /* Takes a FILE* returned by pipe_open, and returns <0 if no process can be
138  * found, 0 on success, and an errno on kill(2) failure */
139 EXPORT s32 mx_fs_pipe_signal(FILE *fp, s32 sig);
140 
141 /* Close fp, which has been opened by fs_pipe_open().
142  * With dowait returns true only upon successful program exit.
143  * In conjunction with CHILD_FD_PASS dowait is mandatory. */
144 EXPORT boole mx_fs_pipe_close(FILE *fp, boole dowait);
145 
146 /* Flush the given or (if NIL) all streams */
147 EXPORT boole mx_fs_flush(FILE *fp);
148 
149 /* Close all files and pipes created without O_NOREGISTER */
150 EXPORT void mx_fs_close_all(void);
151 
152 /* XXX Temporary (pre v15 I/O) line buffer "pool".
153  * (Possibly) Get a line buffer, and release one to the pool, respectively.
154  * _book() returns false for integer overflow, or if reallocation survives
155  * su_STATE_ERR_NONMEM.
156  * The last is driven by the mainloop to perform cleanups */
157 EXPORT void mx_fs_linepool_aquire(char **dp, uz *dsp);
158 EXPORT void mx_fs_linepool_release(char *dp, uz ds);
159 EXPORT boole mx_fs_linepool_book(char **dp, uz *dsp, uz len, uz toadd
160       su_DBG_LOC_ARGS_DECL);
161 EXPORT void mx_fs_linepool_cleanup(boole completely);
162 
163 #ifdef su_HAVE_DBG_LOC_ARGS
164 # define mx_fs_linepool_book(A,B,C,D) \
165    mx_fs_linepool_book(A, B, C, D  su_DBG_LOC_ARGS_INJ)
166 #endif
167 
168 /* TODO The rest below is old-style (will vanish with I/O layer rewrite) */
169 
170 /* fgets() replacement to handle lines of arbitrary size and with embedded \0
171  * characters.
172  * line - line buffer.  *line may be NIL.
173  * linesize - allocated size of line buffer.
174  * count - maximum characters to read.  May be NIL.
175  * llen_or_nil - length_of_line(*line); set to 0 on entry if set.
176  * fp - input FILE.
177  * appendnl - always terminate line with \n, append if necessary.
178  * Manages the n_PS_READLINE_NL hack */
179 EXPORT char *fgetline(char **line, uz *linesize, uz *count, uz *llen_or_nil,
180       FILE *fp, int appendnl  su_DBG_LOC_ARGS_DECL);
181 #ifdef su_HAVE_DBG_LOC_ARGS
182 # define fgetline(A,B,C,D,E,F)   \
183    fgetline(A, B, C, D, E, F  su_DBG_LOC_ARGS_INJ)
184 #endif
185 
186 /* Read up a line from the specified input into the linebuffer.
187  * Return the number of characters read.  Do not include the newline at EOL.
188  * n is the number of characters already read and present in *linebuf; it'll be
189  * treated as _the_ line if no more (successful) reads are possible.
190  * Manages the n_PS_READLINE_NL hack */
191 EXPORT int readline_restart(FILE *ibuf, char **linebuf, uz *linesize, uz n
192       su_DBG_LOC_ARGS_DECL);
193 #ifdef su_HAVE_DBG_LOC_ARGS
194 # define readline_restart(A,B,C,D) \
195    readline_restart(A, B, C, D  su_DBG_LOC_ARGS_INJ)
196 #endif
197 
198 /* Determine the size of the file possessed by the passed buffer */
199 EXPORT off_t fsize(FILE *iob);
200 
201 #include <su/code-ou.h>
202 #endif /* mx_FILE_STREAMS_H */
203 /* s-it-mode */
204