1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2010-2021 Free Software Foundation, Inc.
3
4 This library is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public License
15 along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
16
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20
21 #include <unistd.h>
22 #include <fcntl.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <mailutils/types.h>
26 #include <mailutils/errno.h>
27 #include <mailutils/log.h>
28 #include <mailutils/stream.h>
29 #include <mailutils/stdstream.h>
30 #include <mailutils/util.h>
31 #include <mailutils/io.h>
32 #include <mailutils/filter.h>
33 #include <mailutils/sys/stream.h>
34 #include <mailutils/sys/file_stream.h>
35 #include <mailutils/sys/logstream.h>
36
37 static void stdstream_flushall_setup (void);
38
39 /* This event callback bootstraps standard I/O streams mu_strin and
40 mu_strout. It is invoked when the stream core emits the bootstrap
41 event for the stream. */
42 static void
std_bootstrap(struct _mu_stream * str,int code,unsigned long lval,void * pval)43 std_bootstrap (struct _mu_stream *str, int code,
44 unsigned long lval, void *pval)
45 {
46 struct _mu_file_stream *fstr = (struct _mu_file_stream *) str;
47 _mu_file_stream_setup (fstr);
48 str->event_cb = NULL;
49 str->event_mask = 0;
50 str->event_cb_data = 0;
51 fstr->stream.flags |= _MU_STR_OPEN;
52 mu_stream_set_buffer ((mu_stream_t) fstr, mu_buffer_line, 0);
53 stdstream_flushall_setup ();
54 }
55
56 /* This event callback bootstraps standard error stream (mu_strerr).
57 It is invoked when the stream core emits the bootstrap event for
58 the stream. */
59 static void
std_log_bootstrap(struct _mu_stream * str,int code,unsigned long lval,void * pval)60 std_log_bootstrap (struct _mu_stream *str, int code,
61 unsigned long lval, void *pval)
62 {
63 struct _mu_log_stream *logstr = (struct _mu_log_stream *) str;
64 int yes = 1;
65 mu_stream_t errstr, transport;
66 int rc;
67
68 rc = mu_stdio_stream_create (&errstr, MU_STDERR_FD, 0);
69 if (rc)
70 {
71 fprintf (stderr, "%s: cannot open error stream: %s\n",
72 mu_program_name ? mu_program_name : "<unknown>",
73 mu_strerror (rc));
74 abort ();
75 }
76 /* Make sure 2 is not closed when errstr is destroyed. */
77 mu_stream_ioctl (errstr, MU_IOCTL_FD, MU_IOCTL_FD_SET_BORROW, &yes);
78 if (!mu_program_name)
79 transport = errstr;
80 else
81 {
82 char *fltargs[3] = { "INLINE-COMMENT", };
83 mu_asprintf (&fltargs[1], "%s: ", mu_program_name);
84 fltargs[2] = NULL;
85 rc = mu_filter_create_args (&transport, errstr,
86 "INLINE-COMMENT",
87 2, (const char**)fltargs,
88 MU_FILTER_ENCODE, MU_STREAM_WRITE);
89 mu_stream_unref (errstr);
90 free (fltargs[1]);
91 if (rc)
92 {
93 fprintf (stderr,
94 "%s: cannot open output filter stream: %s",
95 mu_program_name ? mu_program_name : "<unknown>",
96 mu_strerror (rc));
97 abort ();
98 }
99 mu_stream_set_buffer (transport, mu_buffer_line, 0);
100 }
101
102 str->event_cb = NULL;
103 str->event_mask = 0;
104 str->event_cb_data = 0;
105
106 _mu_log_stream_setup (logstr, transport);
107 stdstream_flushall_setup ();
108 }
109
110 /* The noop destroy function is necessary to prevent stream core from
111 freeing the stream on mu_stream_unref. */
112 static void
bootstrap_destroy(struct _mu_stream * str)113 bootstrap_destroy (struct _mu_stream *str)
114 {
115 /* Nothing */
116 }
117
118 /* Standard I/O streams: */
119 static struct _mu_file_stream stdstream[2] = {
120 { .stream = {
121 .ref_count = 1,
122 .buftype = mu_buffer_none,
123 .flags = MU_STREAM_READ,
124 .destroy = bootstrap_destroy,
125 .event_cb = std_bootstrap,
126 .event_mask = _MU_STR_EVMASK (_MU_STR_EVENT_BOOTSTRAP)
127 },
128 .fd = MU_STDIN_FD,
129 .filename = "<stdin>",
130 .flags = _MU_FILE_STREAM_FD_BORROWED|_MU_FILE_STREAM_STATIC_FILENAME },
131 { .stream = {
132 .ref_count = 1,
133 .buftype = mu_buffer_none,
134 .flags = MU_STREAM_WRITE,
135 .destroy = bootstrap_destroy,
136 .event_cb = std_bootstrap,
137 .event_mask = _MU_STR_EVMASK (_MU_STR_EVENT_BOOTSTRAP)
138 },
139 .fd = MU_STDOUT_FD,
140 .filename = "<stdout>",
141 .flags = _MU_FILE_STREAM_FD_BORROWED|_MU_FILE_STREAM_STATIC_FILENAME }
142 };
143
144 /* Standard error stream: */
145 static struct _mu_log_stream default_strerr = {
146 .base = {
147 .ref_count = 1,
148 .buftype = mu_buffer_none,
149 .flags = MU_STREAM_WRITE,
150 .destroy = bootstrap_destroy,
151 .event_cb = std_log_bootstrap,
152 .event_mask = _MU_STR_EVMASK (_MU_STR_EVENT_BOOTSTRAP)
153 }
154 };
155
156 /* Pointers to these: */
157 mu_stream_t mu_strin = (mu_stream_t) &stdstream[MU_STDIN_FD];
158 mu_stream_t mu_strout = (mu_stream_t) &stdstream[MU_STDOUT_FD];
159 mu_stream_t mu_strerr = (mu_stream_t) &default_strerr;
160
161 static void
stdstream_flushall(void * data MU_ARG_UNUSED)162 stdstream_flushall (void *data MU_ARG_UNUSED)
163 {
164 mu_stream_flush (mu_strin);
165 mu_stream_flush (mu_strout);
166 mu_stream_flush (mu_strerr);
167 }
168
169 static void
stdstream_flushall_setup(void)170 stdstream_flushall_setup (void)
171 {
172 static int _setup = 0;
173
174 if (!_setup)
175 {
176 mu_onexit (stdstream_flushall, NULL);
177 _setup = 1;
178 }
179 }
180
181 void
mu_stdstream_setup(int flags)182 mu_stdstream_setup (int flags)
183 {
184 int rc;
185 int fd;
186 int yes = 1;
187
188 /* If the streams are already open, close them */
189 if (flags & MU_STDSTREAM_RESET_STRIN)
190 mu_stream_destroy (&mu_strin);
191 if (flags & MU_STDSTREAM_RESET_STROUT)
192 mu_stream_destroy (&mu_strout);
193 if (flags & MU_STDSTREAM_RESET_STRERR)
194 mu_stream_destroy (&mu_strerr);
195
196 /* Ensure that first 3 descriptors are open in proper mode */
197 fd = open ("/dev/null", O_RDWR);
198 switch (fd)
199 {
200 case 0:
201 /* Keep it and try to open 1 */
202 fd = open ("/dev/null", O_WRONLY);
203 if (fd != 1)
204 {
205 if (fd > 2)
206 close (fd);
207 break;
208 }
209
210 case 1:
211 /* keep it open and try 2 */
212 fd = open ("/dev/null", O_WRONLY);
213 if (fd != 2)
214 close (fd);
215 break;
216
217 case 2:
218 /* keep it open */;
219 break;
220
221 default:
222 close (fd);
223 break;
224 }
225
226 /* Create the corresponding streams */
227 if (!mu_strin)
228 {
229 rc = mu_stdio_stream_create (&mu_strin, MU_STDIN_FD, 0);
230 if (rc)
231 {
232 fprintf (stderr, "mu_stdio_stream_create(%d): %s\n",
233 MU_STDIN_FD, mu_strerror (rc));
234 abort ();
235 }
236 mu_stream_ioctl (mu_strin, MU_IOCTL_FD, MU_IOCTL_FD_SET_BORROW, &yes);
237 }
238
239 if (!mu_strout)
240 {
241 rc = mu_stdio_stream_create (&mu_strout, MU_STDOUT_FD, 0);
242 if (rc)
243 {
244 fprintf (stderr, "mu_stdio_stream_create(%d): %s\n",
245 MU_STDOUT_FD, mu_strerror (rc));
246 abort ();
247 }
248 mu_stream_ioctl (mu_strout, MU_IOCTL_FD, MU_IOCTL_FD_SET_BORROW, &yes);
249 }
250
251 if (!mu_strerr)
252 {
253 if (mu_stdstream_strerr_create (&mu_strerr, MU_STRERR_STDERR, 0, 0,
254 mu_program_name, NULL))
255 abort ();
256 }
257
258 stdstream_flushall_setup ();
259 }
260
261 int
mu_printf(const char * fmt,...)262 mu_printf (const char *fmt, ...)
263 {
264 int rc;
265 va_list ap;
266
267 va_start (ap, fmt);
268 rc = mu_stream_vprintf (mu_strout, fmt, ap);
269 va_end (ap);
270 return rc;
271 }
272