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