Commit 04c2931b 04c2931bb963fefd4654ef2de2cf0667cf8a333d by Sergey Poznyakoff

Rewrite prog stream support.

Two interfaces are provided: mu_prog_stream_create offers full control
over the program execution environment (i.e. running privileges, cwd,
resource limits) via a set of hints.
A simpler interface, mu_command_stream_create, runs the command in the
current environment.

mu_filter_prog_stream_create is removed, because its functionality can
be achieved by a correspondingly crafted set of hints to
mu_prog_stream_create.

* include/mailutils/prog.h: New file.
* include/mailutils/mailutils.h: Include mailutils/prog.h
* include/mailutils/Makefile.am (pkginclude_HEADERS): Add prog.h
* include/mailutils/stream.h (mu_prog_stream_create)
(mu_filter_prog_stream_create): Remove prototypes.
* include/mailutils/sys/prog_stream.h (_mu_prog_stream): Change structure.
(_mu_prog_limit_codes, _mu_prog_limit_flags): New externs.
* include/mailutils/util.h (mu_set_user_privileges)
(mu_switch_to_privs): New prototypes.

* lib/userprivs.c: Move to libmailutils/base.
* lib/Makefile.am (libmuaux_a_SOURCES): Remove userprivs.c
* libmailutils/base/Makefile.am (libbase_la_SOURCES): Add userprivs.c
* libmailutils/base/userprivs.c (mu_set_user_privileges): New function.
(mu_switch_to_privs): Rewrite as another entry point to mu_set_user_privileges.

* libmailutils/stream/prog_stream.c (_mu_prog_limit_flags)
(_mu_prog_limit_codes): New global variables.
(start_program_filter): Use hints to control execution environment.
(_prog_stream_create): Save hints.
(mu_prog_stream_create): Change signature.
(mu_command_stream_create): New function (corresponds to the prior
mu_prog_stream_create).
(mu_filter_prog_stream_create): Remove function.
* comsat/action.c (action_exec): Use new mu_prog_stream_create calling
convention.
* examples/murun.c: Rewrite.

* mh/mhn.c (show_internal): Use new mu_prog_stream_create calling
convention.
* mh/tests/mhn.at: Reflect changes to mhn.

* imap4d/preauth.c: Use mu_command_stream_create.
* libmu_sieve/extensions/pipe.c
* mail/decode.c
* mail/pipe.c
* mail/send.c
* mh/mhl.c
* mu/shell.c

* mail/mail.h: Include mailutils/prog.h
* mh/mh.h

* po/POTFILES.in: Add libmailutils/base/userprivs.c.
1 parent ac08a857
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
18 #include "comsat.h" 18 #include "comsat.h"
19 #include <mailutils/io.h> 19 #include <mailutils/io.h>
20 #include <mailutils/argcv.h> 20 #include <mailutils/argcv.h>
21 #include <mailutils/prog.h>
21 #define obstack_chunk_alloc malloc 22 #define obstack_chunk_alloc malloc
22 #define obstack_chunk_free free 23 #define obstack_chunk_free free
23 #include <obstack.h> 24 #include <obstack.h>
...@@ -225,7 +226,6 @@ action_exec (mu_stream_t tty, int argc, char **argv) ...@@ -225,7 +226,6 @@ action_exec (mu_stream_t tty, int argc, char **argv)
225 { 226 {
226 mu_stream_t pstream; 227 mu_stream_t pstream;
227 struct stat stb; 228 struct stat stb;
228 char *command;
229 int status; 229 int status;
230 230
231 if (argc == 0) 231 if (argc == 0)
...@@ -249,25 +249,19 @@ action_exec (mu_stream_t tty, int argc, char **argv) ...@@ -249,25 +249,19 @@ action_exec (mu_stream_t tty, int argc, char **argv)
249 249
250 if (stb.st_mode & (S_ISUID|S_ISGID)) 250 if (stb.st_mode & (S_ISUID|S_ISGID))
251 { 251 {
252 mu_diag_output (MU_DIAG_ERROR, _("will not execute set[ug]id programs")); 252 mu_diag_output (MU_DIAG_ERROR,
253 _("will not execute set[ug]id programs"));
253 return; 254 return;
254 } 255 }
255 256
256 /* FIXME: Redirect stderr to out */ 257 status = mu_prog_stream_create (&pstream,
257 /* FIXME: need this: 258 argv[0], argc, argv,
258 status = mu_prog_stream_create_argv (&pstream, argv[0], argv, 259 MU_PROG_HINT_ERRTOOUT,
260 NULL,
259 MU_STREAM_READ); 261 MU_STREAM_READ);
260 */
261 status = mu_argcv_join (argc, argv, " ", mu_argcv_escape_no, &command);
262 if (status) 262 if (status)
263 { 263 {
264 mu_diag_funcall (MU_DIAG_ERROR, "mu_argcv_join", NULL, status); 264 mu_diag_funcall (MU_DIAG_ERROR, "mu_prog_stream_create", argv[0],
265 return;
266 }
267 status = mu_prog_stream_create (&pstream, command, MU_STREAM_READ);
268 if (status)
269 {
270 mu_diag_funcall (MU_DIAG_ERROR, "mu_prog_stream_create_argv", argv[0],
271 status); 265 status);
272 return; 266 return;
273 } 267 }
......
...@@ -24,20 +24,7 @@ ...@@ -24,20 +24,7 @@
24 #include <ctype.h> 24 #include <ctype.h>
25 #include <string.h> 25 #include <string.h>
26 #include <mailutils/mailutils.h> 26 #include <mailutils/mailutils.h>
27 #include <mailutils/argcv.h> 27 #include <mailutils/sys/prog_stream.h>
28
29 static void
30 read_and_print (mu_stream_t in, mu_stream_t out)
31 {
32 size_t size;
33 char buffer[128];
34
35 while (mu_stream_readline (in, buffer, sizeof (buffer), &size) == 0
36 && size > 0)
37 {
38 mu_stream_write (out, buffer, size, NULL);
39 }
40 }
41 28
42 int 29 int
43 main (int argc, char *argv[]) 30 main (int argc, char *argv[])
...@@ -45,34 +32,154 @@ main (int argc, char *argv[]) ...@@ -45,34 +32,154 @@ main (int argc, char *argv[])
45 int rc; 32 int rc;
46 mu_stream_t stream, out; 33 mu_stream_t stream, out;
47 int read_stdin = 0; 34 int read_stdin = 0;
48 int i = 1; 35 int i;
49 char *cmdline;
50 int flags = MU_STREAM_READ; 36 int flags = MU_STREAM_READ;
37 struct mu_prog_hints hints;
38 int hint_flags = 0;
39 char *progname = NULL;
40 gid_t gid[20];
41 size_t gn = 0;
51 42
52 if (argc > 1 && strcmp (argv[i], "--stdin") == 0) 43 for (i = 1; i < argc; i++)
44 {
45 if (strcmp (argv[i], "--stdin") == 0)
53 { 46 {
54 read_stdin = 1; 47 read_stdin = 1;
55 flags |= MU_STREAM_WRITE; 48 flags |= MU_STREAM_WRITE;
49 }
50 else if (strcmp (argv[i], "--chdir") == 0)
51 {
52 hints.mu_prog_workdir = argv[i+1];
53 hint_flags |= MU_PROG_HINT_WORKDIR;
56 i++; 54 i++;
57 } 55 }
56 else if (strncmp (argv[i], "--limit", 7) == 0
57 && mu_isdigit (argv[i][7]))
58 {
59 int n;
60
61 if (i + 1 == argc)
62 {
63 fprintf (stderr, "%s requires argument\n", argv[i]);
64 exit (1);
65 }
66 n = argv[i][7] - '0';
67 if (!(_mu_prog_limit_flags & MU_PROG_HINT_LIMIT(n)))
68 {
69 fprintf (stderr, "%s is not supported\n", argv[i]+2);
70 continue;
71 }
72 hint_flags |= MU_PROG_HINT_LIMIT(n);
73 hints.mu_prog_limit[n] = strtoul (argv[i+1], NULL, 10);
74 i++;
75 }
76 else if (strcmp (argv[i], "--prio") == 0)
77 {
78 if (i + 1 == argc)
79 {
80 fprintf (stderr, "%s requires argument\n", argv[i]);
81 exit (1);
82 }
83 hint_flags |= MU_PROG_HINT_PRIO;
84 hints.mu_prog_prio = strtoul (argv[i+1], NULL, 10);
85 i++;
86 }
87 else if (strcmp (argv[i], "--exec") == 0)
88 {
89 if (i + 1 == argc)
90 {
91 fprintf (stderr, "%s requires argument\n", argv[i]);
92 exit (1);
93 }
94 progname = argv[++i];
95 }
96 else if (strcmp (argv[i], "--errignore") == 0)
97 hint_flags |= MU_PROG_HINT_IGNOREFAIL;
98 else if (strcmp (argv[i], "--uid") == 0)
99 {
100 if (i + 1 == argc)
101 {
102 fprintf (stderr, "%s requires argument\n", argv[i]);
103 exit (1);
104 }
105 hint_flags |= MU_PROG_HINT_UID;
106 hints.mu_prog_uid = strtoul (argv[i+1], NULL, 10);
107 i++;
108 }
109 else if (strcmp (argv[i], "--gid") == 0)
110 {
111 mu_list_t list;
112 mu_iterator_t itr;
113
114 if (i + 1 == argc)
115 {
116 fprintf (stderr, "%s requires argument\n", argv[i]);
117 exit (1);
118 }
119 mu_list_create (&list);
120 mu_list_set_destroy_item (list, mu_list_free_item);
121 rc = mu_string_split (argv[++i], ",", list);
122 if (mu_list_get_iterator (list, &itr) == 0)
123 {
124 char *p;
125
126 for (mu_iterator_first (itr);
127 !mu_iterator_is_done (itr); mu_iterator_next (itr))
128 {
129 if (gn >= MU_ARRAY_SIZE (gid))
130 {
131 fprintf (stderr, "too many gids\n");
132 exit (1);
133 }
134 gid[gn++] = strtoul (p, NULL, 10);
135 }
136 mu_iterator_destroy (&itr);
137 }
138 else
139 {
140 mu_diag_funcall (MU_DIAG_ERROR, "mu_list_get_iterator", NULL,
141 rc);
142 exit (1);
143 }
144 mu_list_destroy (&list);
145 hint_flags |= MU_PROG_HINT_GID;
146 }
147 else if (strcmp (argv[i], "--") == 0)
148 {
149 i++;
150 break;
151 }
152 else
153 break;
154 }
58 155
59 if (i == argc) 156 if (i == argc)
60 { 157 {
61 fprintf (stderr, "Usage: %s [--stdin] progname [args]\n", argv[0]); 158 fprintf (stderr,
159 "Usage: %s [--stdin] [--chdir dir] [--limit{0-9} lim] [--prio N]\n"
160 " [--exec progname] progname [args]\n", argv[0]);
62 exit (1); 161 exit (1);
63 } 162 }
64 163
65 MU_ASSERT (mu_argcv_string (argc - i, &argv[i], &cmdline)); 164 argc -= i;
165 argv += i;
166
167 if (!progname)
168 progname = argv[0];
169
66 if (read_stdin) 170 if (read_stdin)
67 { 171 {
68 mu_stream_t in; 172 MU_ASSERT (mu_stdio_stream_create (&hints.mu_prog_input,
69 MU_ASSERT (mu_stdio_stream_create (&in, MU_STDIN_FD, 0)); 173 MU_STDIN_FD, 0));
70 rc = mu_filter_prog_stream_create (&stream, cmdline, in); 174 hint_flags |= MU_PROG_HINT_INPUT;
71 /* Make sure closing/destroying stream will close/destroy in */
72 mu_stream_unref (in);
73 } 175 }
74 else 176
75 rc = mu_prog_stream_create (&stream, cmdline, flags); 177 rc = mu_prog_stream_create (&stream, progname, argc, argv,
178 hint_flags, &hints, flags);
179 if (hint_flags & MU_PROG_HINT_INPUT)
180 /* Make sure closing/destroying stream will close/destroy input */
181 mu_stream_unref (hints.mu_prog_input);
182
76 if (rc) 183 if (rc)
77 { 184 {
78 fprintf (stderr, "%s: cannot create program filter stream: %s\n", 185 fprintf (stderr, "%s: cannot create program filter stream: %s\n",
...@@ -82,7 +189,7 @@ main (int argc, char *argv[]) ...@@ -82,7 +189,7 @@ main (int argc, char *argv[])
82 189
83 MU_ASSERT (mu_stdio_stream_create (&out, MU_STDOUT_FD, 0)); 190 MU_ASSERT (mu_stdio_stream_create (&out, MU_STDOUT_FD, 0));
84 191
85 read_and_print (stream, out); 192 mu_stream_copy (out, stream, 0, NULL);
86 mu_stream_close (stream); 193 mu_stream_close (stream);
87 mu_stream_destroy (&stream); 194 mu_stream_destroy (&stream);
88 mu_stream_close (out); 195 mu_stream_close (out);
......
...@@ -448,7 +448,7 @@ do_preauth_program (struct sockaddr *pcs, struct sockaddr *sa) ...@@ -448,7 +448,7 @@ do_preauth_program (struct sockaddr *pcs, struct sockaddr *sa)
448 return NULL; 448 return NULL;
449 } 449 }
450 450
451 rc = mu_prog_stream_create (&str, ws.ws_wordv[0], MU_STREAM_READ); 451 rc = mu_command_stream_create (&str, ws.ws_wordv[0], MU_STREAM_READ);
452 mu_wordsplit_free (&ws); 452 mu_wordsplit_free (&ws);
453 if (rc) 453 if (rc)
454 { 454 {
......
...@@ -77,6 +77,7 @@ pkginclude_HEADERS = \ ...@@ -77,6 +77,7 @@ pkginclude_HEADERS = \
77 pam.h\ 77 pam.h\
78 parse822.h\ 78 parse822.h\
79 pop3.h\ 79 pop3.h\
80 prog.h\
80 progmailer.h\ 81 progmailer.h\
81 property.h\ 82 property.h\
82 python.h\ 83 python.h\
......
...@@ -66,5 +66,6 @@ ...@@ -66,5 +66,6 @@
66 #include <mailutils/wordsplit.h> 66 #include <mailutils/wordsplit.h>
67 #include <mailutils/log.h> 67 #include <mailutils/log.h>
68 #include <mailutils/stdstream.h> 68 #include <mailutils/stdstream.h>
69 #include <mailutils/prog.h>
69 70
70 /* EOF */ 71 /* EOF */
......
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2009 Free Software Foundation, Inc.
3
4 GNU Mailutils is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 GNU Mailutils 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 General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
16
17 #ifndef _MAILUTILS_PROG_H
18 #define _MAILUTILS_PROG_H
19
20 #include <sys/time.h>
21 #include <sys/resource.h>
22 #include <mailutils/types.h>
23
24 #define MU_PROG_LIMIT_AS 0
25 #define MU_PROG_LIMIT_CPU 1
26 #define MU_PROG_LIMIT_DATA 2
27 #define MU_PROG_LIMIT_FSIZE 3
28 #define MU_PROG_LIMIT_NPROC 4
29 #define MU_PROG_LIMIT_CORE 5
30 #define MU_PROG_LIMIT_MEMLOCK 6
31 #define MU_PROG_LIMIT_NOFILE 7
32 #define MU_PROG_LIMIT_RSS 8
33 #define MU_PROG_LIMIT_STACK 9
34
35 #define _MU_PROG_LIMIT_MAX 10
36
37 #define MU_PROG_HINT_WORKDIR 0x0001 /* Workdir is set */
38 #define MU_PROG_HINT_PRIO 0x0002 /* Prio is set */
39 #define MU_PROG_HINT_INPUT 0x0004 /* Input stream is set */
40 #define MU_PROG_HINT_UID 0x0008 /* Uid is set */
41 #define MU_PROG_HINT_GID 0x0010 /* Supplementary gids are set */
42 #define MU_PROG_HINT_ERRTOOUT 0x0020 /* Redirect stderr to stdout */
43 #define MU_PROG_HINT_ERRTOSTREAM 0x0040 /* Redirect stderr to errstream */
44 #define MU_PROG_HINT_IGNOREFAIL 0x0080 /* Ignore hint setup failures */
45 #define _MU_PROG_HINT_MASK 0x00ff
46 #define MU_PROG_HINT_LIMIT(n) (0x100 << (n)) /* MU_PROG_LIMIT_n is set */
47
48 struct mu_prog_hints
49 {
50 char *mu_prog_workdir; /* Working directory */
51 uid_t mu_prog_uid; /* Run as this user */
52 gid_t *mu_prog_gidv; /* Array of supplementary gids */
53 size_t mu_prog_gidc; /* Number of elements in gidv */
54 rlim_t mu_prog_limit[_MU_PROG_LIMIT_MAX]; /* Limits */
55 int mu_prog_prio; /* Scheduling priority */
56 mu_stream_t mu_prog_input; /* Input stream */
57 mu_stream_t mu_prog_error; /* Error stream */
58 };
59
60 int mu_prog_stream_create (mu_stream_t *pstream,
61 const char *progname,
62 size_t argc, char **argv,
63 int hflags,
64 struct mu_prog_hints *hints,
65 int flags);
66 int mu_command_stream_create (mu_stream_t *pstream, const char *command,
67 int flags);
68
69 #endif
70
...@@ -250,9 +250,6 @@ int mu_fd_stream_create (mu_stream_t *pstream, char *filename, int fd, ...@@ -250,9 +250,6 @@ int mu_fd_stream_create (mu_stream_t *pstream, char *filename, int fd,
250 #define MU_STDERR_FD 2 250 #define MU_STDERR_FD 2
251 int mu_stdio_stream_create (mu_stream_t *pstream, int fd, int flags); 251 int mu_stdio_stream_create (mu_stream_t *pstream, int fd, int flags);
252 252
253 int mu_prog_stream_create (mu_stream_t *pstream, const char *progname, int flags);
254 int mu_filter_prog_stream_create (mu_stream_t *pstream, const char *progname,
255 mu_stream_t input);
256 int mu_memory_stream_create (mu_stream_t *pstream, int flags); 253 int mu_memory_stream_create (mu_stream_t *pstream, int flags);
257 int mu_static_memory_stream_create (mu_stream_t *pstream, const void *mem, 254 int mu_static_memory_stream_create (mu_stream_t *pstream, const void *mem,
258 size_t size); 255 size_t size);
......
...@@ -18,18 +18,25 @@ ...@@ -18,18 +18,25 @@
18 #define _MAILUTILS_SYS_PROG_STREAM_H 18 #define _MAILUTILS_SYS_PROG_STREAM_H
19 19
20 #include <mailutils/sys/stream.h> 20 #include <mailutils/sys/stream.h>
21 #include <mailutils/prog.h>
21 22
22 struct _mu_prog_stream 23 struct _mu_prog_stream
23 { 24 {
24 struct _mu_stream stream; 25 struct _mu_stream stream; /* Base stream */
26 char *progname; /* Program name */
27 size_t argc; /* Number of arguments */
28 char **argv; /* Program arguments */
29 int hint_flags; /* Hint flags */
30 struct mu_prog_hints hints; /* Invocation hints */
31
25 pid_t pid; 32 pid_t pid;
26 int status; 33 int status;
27 pid_t writer_pid; 34 pid_t writer_pid;
28 int argc;
29 char **argv;
30 mu_stream_t in, out;
31 35
32 mu_stream_t input; 36 mu_stream_t in, out;
33 }; 37 };
34 38
39 extern int _mu_prog_limit_flags;
40 extern int _mu_prog_limit_codes[_MU_PROG_LIMIT_MAX];
41
35 #endif 42 #endif
......
...@@ -175,8 +175,10 @@ int mu_getpass (mu_stream_t in, mu_stream_t out, const char *prompt, ...@@ -175,8 +175,10 @@ int mu_getpass (mu_stream_t in, mu_stream_t out, const char *prompt,
175 /* ----------------------- */ 175 /* ----------------------- */
176 /* Get the host name, doing a gethostbyname() if possible. */ 176 /* Get the host name, doing a gethostbyname() if possible. */
177 int mu_get_host_name (char **host); 177 int mu_get_host_name (char **host);
178 int mu_spawnvp(const char *prog, char *av[], int *stat); 178 int mu_spawnvp (const char *prog, char *av[], int *stat);
179 int mu_scheme_autodetect_p (mu_url_t); 179 int mu_scheme_autodetect_p (mu_url_t);
180 int mu_set_user_privileges (uid_t uid, gid_t *gidv, size_t gidc);
181 int mu_switch_to_privs (uid_t uid, gid_t gid, mu_list_t retain_groups);
180 182
181 struct timeval; 183 struct timeval;
182 int mu_fd_wait (int fd, int *pflags, struct timeval *tvp); 184 int mu_fd_wait (int fd, int *pflags, struct timeval *tvp);
......
...@@ -27,7 +27,6 @@ libmuaux_a_SOURCES += \ ...@@ -27,7 +27,6 @@ libmuaux_a_SOURCES += \
27 signal.c\ 27 signal.c\
28 strexit.c\ 28 strexit.c\
29 tcpwrap.c\ 29 tcpwrap.c\
30 userprivs.c\
31 mu_umaxtostr.c\ 30 mu_umaxtostr.c\
32 mu_umaxtostr.h 31 mu_umaxtostr.h
33 32
......
...@@ -63,6 +63,7 @@ libbase_la_SOURCES = \ ...@@ -63,6 +63,7 @@ libbase_la_SOURCES = \
63 tempfile.c\ 63 tempfile.c\
64 ticket.c\ 64 ticket.c\
65 tilde.c\ 65 tilde.c\
66 userprivs.c\
66 usremail.c\ 67 usremail.c\
67 version.c\ 68 version.c\
68 wicket.c 69 wicket.c
......
...@@ -29,75 +29,90 @@ ...@@ -29,75 +29,90 @@
29 #include <mailutils/nls.h> 29 #include <mailutils/nls.h>
30 #include <mailutils/list.h> 30 #include <mailutils/list.h>
31 #include <mailutils/iterator.h> 31 #include <mailutils/iterator.h>
32 #include <xalloc.h>
33 32
34 /* Switch to the given UID/GID */ 33 /* Switch to the given UID/GID */
35 int 34 int
36 mu_switch_to_privs (uid_t uid, gid_t gid, mu_list_t retain_groups) 35 mu_set_user_privileges (uid_t uid, gid_t *gidv, size_t gidc)
37 { 36 {
38 int rc = 0; 37 int rc = 0;
39 gid_t *emptygidset; 38 gid_t gid;
40 size_t size = 1, j = 1; 39
41 mu_iterator_t itr; 40 if (getuid ())
41 return EACCES;
42 42
43 if (uid == 0) 43 if (uid == 0)
44 return 0; 44 return 0;
45 45
46 /* Create a list of supplementary groups */
47 mu_list_count (retain_groups, &size);
48 size++;
49 emptygidset = xmalloc (size * sizeof emptygidset[0]);
50 emptygidset[0] = gid ? gid : getegid ();
51
52 if (mu_list_get_iterator (retain_groups, &itr) == 0)
53 {
54 for (mu_iterator_first (itr);
55 !mu_iterator_is_done (itr); mu_iterator_next (itr))
56 mu_iterator_current (itr,
57 (void **)(emptygidset + j++));
58 mu_iterator_destroy (&itr);
59 }
60
61 /* Reset group permissions */ 46 /* Reset group permissions */
62 if (geteuid () == 0 && setgroups (j, emptygidset)) 47 if (gidv && gidc)
48 {
49 if (geteuid () == 0 && setgroups (gidc, gidv))
63 { 50 {
64 mu_error(_("setgroups(1, %lu) failed: %s"), 51 mu_error(_("setgroups(1, %lu) failed: %s"),
65 (unsigned long) emptygidset[0], mu_strerror (errno)); 52 (unsigned long) gidv[0], mu_strerror (errno));
66 rc = 1; 53 return errno;
54 }
55 gid = gidv[0];
56 }
57 else
58 {
59 struct passwd *pwd = getpwuid (uid);
60 if (pwd)
61 gid = pwd->pw_gid;
62 else
63 gid = getegid ();
67 } 64 }
68 free (emptygidset);
69 65
70 /* Switch to the user's gid. On some OSes the effective gid must 66 /* Switch to the user's gid. On some OSes the effective gid must
71 be reset first */ 67 be reset first */
72 68
73 #if defined(HAVE_SETEGID) 69 #if defined(HAVE_SETEGID)
74 if ((rc = setegid (gid)) < 0) 70 if (setegid (gid) < 0)
71 {
72 rc = errno;
75 mu_error (_("setegid(%lu) failed: %s"), 73 mu_error (_("setegid(%lu) failed: %s"),
76 (unsigned long) gid, mu_strerror (errno)); 74 (unsigned long) gid, mu_strerror (rc));
75 }
77 #elif defined(HAVE_SETREGID) 76 #elif defined(HAVE_SETREGID)
78 if ((rc = setregid (gid, gid)) < 0) 77 if (setregid (gid, gid) < 0)
78 {
79 rc = errno;
79 mu_error (_("setregid(%lu,%lu) failed: %s"), 80 mu_error (_("setregid(%lu,%lu) failed: %s"),
80 (unsigned long) gid, (unsigned long) gid, 81 (unsigned long) gid, (unsigned long) gid,
81 mu_strerror (errno)); 82 mu_strerror (rc));
83 }
82 #elif defined(HAVE_SETRESGID) 84 #elif defined(HAVE_SETRESGID)
83 if ((rc = setresgid (gid, gid, gid)) < 0) 85 if (setresgid (gid, gid, gid) < 0)
86 {
87 rc = errno;
84 mu_error (_("setresgid(%lu,%lu,%lu) failed: %s"), 88 mu_error (_("setresgid(%lu,%lu,%lu) failed: %s"),
85 (unsigned long) gid, 89 (unsigned long) gid,
86 (unsigned long) gid, 90 (unsigned long) gid,
87 (unsigned long) gid, 91 (unsigned long) gid,
88 mu_strerror (errno)); 92 mu_strerror (rc));
93 }
89 #endif 94 #endif
90 95
91 if (rc == 0 && gid != 0) 96 if (rc == 0 && gid != 0)
92 { 97 {
93 if ((rc = setgid (gid)) < 0 && getegid () != gid) 98 if (setgid (gid) < 0)
99 {
100 rc = errno;
94 mu_error (_("setgid(%lu) failed: %s"), 101 mu_error (_("setgid(%lu) failed: %s"),
95 (unsigned long) gid, mu_strerror (errno)); 102 (unsigned long) gid, mu_strerror (rc));
103 }
104 else if (getegid () != gid)
105 {
106 rc = MU_ERR_FAILURE;
107 mu_error (_("setgid(%lu) failed: %s"),
108 (unsigned long) gid, mu_strerror (rc));
109 }
110
96 if (rc == 0 && getegid () != gid) 111 if (rc == 0 && getegid () != gid)
97 { 112 {
98 mu_error (_("Cannot set effective gid to %lu"), 113 mu_error (_("Cannot set effective gid to %lu"),
99 (unsigned long) gid); 114 (unsigned long) gid);
100 rc = 1; 115 rc = MU_ERR_FAILURE;
101 } 116 }
102 } 117 }
103 118
...@@ -114,24 +129,24 @@ mu_switch_to_privs (uid_t uid, gid_t gid, mu_list_t retain_groups) ...@@ -114,24 +129,24 @@ mu_switch_to_privs (uid_t uid, gid_t gid, mu_list_t retain_groups)
114 { 129 {
115 if (setreuid (uid, -1) < 0) 130 if (setreuid (uid, -1) < 0)
116 { 131 {
132 rc = errno;
117 mu_error (_("setreuid(%lu,-1) failed: %s"), 133 mu_error (_("setreuid(%lu,-1) failed: %s"),
118 (unsigned long) uid, 134 (unsigned long) uid,
119 mu_strerror (errno)); 135 mu_strerror (rc));
120 rc = 1;
121 } 136 }
122 if (setuid (uid) < 0) 137 if (setuid (uid) < 0)
123 { 138 {
139 rc = errno;
124 mu_error (_("second setuid(%lu) failed: %s"), 140 mu_error (_("second setuid(%lu) failed: %s"),
125 (unsigned long) uid, mu_strerror (errno)); 141 (unsigned long) uid, mu_strerror (rc));
126 rc = 1;
127 } 142 }
128 } else 143 } else
129 #endif 144 #endif
130 { 145 {
146 rc = errno;
131 mu_error (_("setuid(%lu) failed: %s"), 147 mu_error (_("setuid(%lu) failed: %s"),
132 (unsigned long) uid, 148 (unsigned long) uid,
133 mu_strerror (errno)); 149 mu_strerror (rc));
134 rc = 1;
135 } 150 }
136 } 151 }
137 152
...@@ -139,15 +154,46 @@ mu_switch_to_privs (uid_t uid, gid_t gid, mu_list_t retain_groups) ...@@ -139,15 +154,46 @@ mu_switch_to_privs (uid_t uid, gid_t gid, mu_list_t retain_groups)
139 if (uid != 0 && setuid (0) == 0) 154 if (uid != 0 && setuid (0) == 0)
140 { 155 {
141 mu_error (_("seteuid(0) succeeded when it should not")); 156 mu_error (_("seteuid(0) succeeded when it should not"));
142 rc = 1; 157 rc = MU_ERR_FAILURE;
143 } 158 }
144 else if (uid != euid && setuid (euid) == 0) 159 else if (uid != euid && setuid (euid) == 0)
145 { 160 {
146 mu_error (_("Cannot drop non-root setuid privileges")); 161 mu_error (_("Cannot drop non-root setuid privileges"));
147 rc = 1; 162 rc = MU_ERR_FAILURE;
148 } 163 }
149 } 164 }
150 return rc; 165 return rc;
151 } 166 }
152 167
168 int
169 mu_switch_to_privs (uid_t uid, gid_t gid, mu_list_t retain_groups)
170 {
171 int rc = 0;
172 gid_t *emptygidset;
173 size_t size = 1, j = 1;
174 mu_iterator_t itr;
175
176 if (uid == 0)
177 return 0;
178
179 /* Create a list of supplementary groups */
180 mu_list_count (retain_groups, &size);
181 size++;
182 emptygidset = malloc (size * sizeof emptygidset[0]);
183 if (!emptygidset)
184 return ENOMEM;
185 emptygidset[0] = gid ? gid : getegid ();
186
187 if (mu_list_get_iterator (retain_groups, &itr) == 0)
188 {
189 for (mu_iterator_first (itr);
190 !mu_iterator_is_done (itr); mu_iterator_next (itr))
191 mu_iterator_current (itr,
192 (void **)(emptygidset + j++));
193 mu_iterator_destroy (&itr);
194 }
195 rc = mu_set_user_privileges (uid, emptygidset, j);
196 free (emptygidset);
197 return rc;
198 }
153 199
......
...@@ -71,9 +71,105 @@ _prog_stream_unregister (struct _mu_prog_stream *stream) ...@@ -71,9 +71,105 @@ _prog_stream_unregister (struct _mu_prog_stream *stream)
71 #define REDIRECT_STDIN_P(f) ((f) & MU_STREAM_WRITE) 71 #define REDIRECT_STDIN_P(f) ((f) & MU_STREAM_WRITE)
72 #define REDIRECT_STDOUT_P(f) ((f) & MU_STREAM_READ) 72 #define REDIRECT_STDOUT_P(f) ((f) & MU_STREAM_READ)
73 73
74 #ifdef RLIMIT_AS
75 # define _MU_RLIMIT_AS_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_AS)
76 #else
77 # define _MU_RLIMIT_AS_FLAG 0
78 # define RLIMIT_AS 0
79 #endif
80
81 #ifdef RLIMIT_CPU
82 # define _MU_RLIMIT_CPU_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_CPU)
83 #else
84 # define _MU_RLIMIT_CPU_FLAG 0
85 # define RLIMIT_CPU 0
86 #endif
87
88 #ifdef RLIMIT_DATA
89 # define _MU_RLIMIT_DATA_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_DATA)
90 #else
91 # define _MU_RLIMIT_DATA_FLAG 0
92 # define RLIMIT_DATA 0
93 #endif
94
95 #ifdef RLIMIT_FSIZE
96 # define _MU_RLIMIT_FSIZE_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_FSIZE)
97 #else
98 # define _MU_RLIMIT_FSIZE_FLAG 0
99 # define RLIMIT_FSIZE 0
100 #endif
101
102 #ifdef RLIMIT_NPROC
103 # define _MU_RLIMIT_NPROC_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_NPROC)
104 #else
105 # define _MU_RLIMIT_NPROC_FLAG 0
106 # define RLIMIT_NPROC 0
107 #endif
108
109 #ifdef RLIMIT_CORE
110 # define _MU_RLIMIT_CORE_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_CORE)
111 #else
112 # define _MU_RLIMIT_CORE_FLAG 0
113 # define RLIMIT_CORE 0
114 #endif
115
116 #ifdef RLIMIT_MEMLOCK
117 # define _MU_RLIMIT_MEMLOCK_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_MEMLOCK)
118 #else
119 # define _MU_RLIMIT_MEMLOCK_FLAG 0
120 # define RLIMIT_MEMLOCK 0
121 #endif
122
123 #ifdef RLIMIT_NOFILE
124 # define _MU_RLIMIT_NOFILE_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_NOFILE)
125 #else
126 # define _MU_RLIMIT_NOFILE_FLAG 0
127 # define RLIMIT_NOFILE 0
128 #endif
129
130 #ifdef RLIMIT_RSS
131 # define _MU_RLIMIT_RSS_FLAG MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_RSS)
132 #else
133 # define _MU_RLIMIT_RSS_FLAG 0
134 # define RLIMIT_RSS 0
135 #endif
136
137 #ifdef RLIMIT_STACK
138 # define _MU_RLIMIT_STACK MU_PROG_HINT_LIMIT(MU_PROG_LIMIT_STACK)
139 #else
140 # define _MU_RLIMIT_STACK 0
141 # define RLIMIT_STACK 0
142 #endif
143
144 #define _MU_PROG_AVAILABLE_LIMITS \
145 (_MU_RLIMIT_AS_FLAG | \
146 _MU_RLIMIT_CPU_FLAG | \
147 _MU_RLIMIT_DATA_FLAG | \
148 _MU_RLIMIT_FSIZE_FLAG | \
149 _MU_RLIMIT_NPROC_FLAG | \
150 _MU_RLIMIT_CORE_FLAG | \
151 _MU_RLIMIT_MEMLOCK_FLAG | \
152 _MU_RLIMIT_NOFILE_FLAG | \
153 _MU_RLIMIT_RSS_FLAG | \
154 _MU_RLIMIT_STACK)
155
156 int _mu_prog_limit_flags = _MU_PROG_AVAILABLE_LIMITS;
157
158 int _mu_prog_limit_codes[_MU_PROG_LIMIT_MAX] = {
159 RLIMIT_AS,
160 RLIMIT_CPU,
161 RLIMIT_DATA,
162 RLIMIT_FSIZE,
163 RLIMIT_NPROC,
164 RLIMIT_CORE,
165 RLIMIT_MEMLOCK,
166 RLIMIT_NOFILE,
167 RLIMIT_RSS,
168 RLIMIT_STACK
169 };
170
74 static int 171 static int
75 start_program_filter (pid_t *pid, int *p, int argc, char **argv, 172 start_program_filter (int *p, struct _mu_prog_stream *fs, int flags)
76 char *errfile, int flags)
77 { 173 {
78 int rightp[2], leftp[2]; 174 int rightp[2], leftp[2];
79 int i; 175 int i;
...@@ -84,7 +180,7 @@ start_program_filter (pid_t *pid, int *p, int argc, char **argv, ...@@ -84,7 +180,7 @@ start_program_filter (pid_t *pid, int *p, int argc, char **argv,
84 if (REDIRECT_STDOUT_P (flags)) 180 if (REDIRECT_STDOUT_P (flags))
85 pipe (rightp); 181 pipe (rightp);
86 182
87 switch (*pid = fork ()) 183 switch (fs->pid = fork ())
88 { 184 {
89 /* The child branch. */ 185 /* The child branch. */
90 case 0: 186 case 0:
...@@ -112,27 +208,68 @@ start_program_filter (pid_t *pid, int *p, int argc, char **argv, ...@@ -112,27 +208,68 @@ start_program_filter (pid_t *pid, int *p, int argc, char **argv,
112 close (leftp[1]); 208 close (leftp[1]);
113 } 209 }
114 210
115 /* Error output */ 211 if (fs->hint_flags & MU_PROG_HINT_ERRTOOUT)
116 if (errfile) 212 dup2 (1, 2);
213
214 if (fs->hint_flags & MU_PROG_HINT_WORKDIR)
117 { 215 {
118 i = open (errfile, O_CREAT|O_WRONLY|O_APPEND, 0644); 216 if (chdir (fs->hints.mu_prog_workdir))
119 if (i > 0 && i != 2)
120 { 217 {
121 dup2 (i, 2); 218 mu_error (_("cannot change to %s: %s"),
122 close (i); 219 fs->hints.mu_prog_workdir, mu_strerror (errno));
220 if (!(fs->hint_flags & MU_PROG_HINT_IGNOREFAIL))
221 _exit (127);
222 }
223 }
224
225 if (fs->hint_flags & MU_PROG_HINT_UID)
226 {
227 if (mu_set_user_privileges (fs->hints.mu_prog_uid,
228 fs->hints.mu_prog_gidv,
229 fs->hints.mu_prog_gidc)
230 && !(fs->hint_flags & MU_PROG_HINT_IGNOREFAIL))
231 _exit (127);
232 }
233
234 for (i = 0; i < _MU_PROG_LIMIT_MAX; i++)
235 {
236 if (MU_PROG_HINT_LIMIT(i) & fs->hint_flags)
237 {
238 struct rlimit rlim;
239
240 rlim.rlim_cur = rlim.rlim_max = fs->hints.mu_prog_limit[i];
241 if (setrlimit (_mu_prog_limit_codes[i], &rlim))
242 {
243 mu_error (_("error setting limit %d to %lu: %s"),
244 i, (unsigned long) rlim.rlim_cur,
245 mu_strerror (errno));
246 if (!(fs->hint_flags & MU_PROG_HINT_IGNOREFAIL))
247 _exit (127);
248 }
123 } 249 }
124 } 250 }
251 if (MU_PROG_HINT_PRIO & fs->hint_flags)
252 {
253 if (setpriority (PRIO_PROCESS, 0, fs->hints.mu_prog_prio))
254 {
255 mu_error (_("error setting priority: %s"),
256 mu_strerror (errno));
257 if (!(fs->hint_flags & MU_PROG_HINT_IGNOREFAIL))
258 _exit (127);
259 }
260 }
261
125 /* Close unneded descripitors */ 262 /* Close unneded descripitors */
126 for (i = getmaxfd (); i > 2; i--) 263 for (i = getmaxfd (); i > 2; i--)
127 close (i); 264 close (i);
128 265
129 /*FIXME: Switch to other uid/gid if desired */ 266 /*FIXME: Switch to other uid/gid if desired */
130 execvp (argv[0], argv); 267 execvp (fs->progname, fs->argv);
131 268
132 /* Report error via syslog */ 269 /* Report error via syslog */
133 syslog (LOG_ERR|LOG_USER, "can't run %s (ruid=%d, euid=%d): %m", 270 syslog (LOG_ERR|LOG_USER, "can't run %s (ruid=%d, euid=%d): %m",
134 argv[0], getuid (), geteuid ()); 271 fs->progname, getuid (), geteuid ());
135 exit (127); 272 _exit (127);
136 /********************/ 273 /********************/
137 274
138 /* Parent branches: */ 275 /* Parent branches: */
...@@ -187,10 +324,16 @@ _prog_wait (pid_t pid, int *pstatus) ...@@ -187,10 +324,16 @@ _prog_wait (pid_t pid, int *pstatus)
187 static void 324 static void
188 _prog_done (mu_stream_t stream) 325 _prog_done (mu_stream_t stream)
189 { 326 {
190 struct _mu_prog_stream *fs = (struct _mu_prog_stream *) stream;
191 int status; 327 int status;
328 struct _mu_prog_stream *fs = (struct _mu_prog_stream *) stream;
192 329
193 mu_argcv_free (fs->argc, fs->argv); 330 mu_argcv_free (fs->argc, fs->argv);
331 free (fs->progname);
332 if (fs->hint_flags & MU_PROG_HINT_WORKDIR)
333 free (fs->hints.mu_prog_workdir);
334 if (fs->hint_flags & MU_PROG_HINT_INPUT)
335 mu_stream_unref (fs->hints.mu_prog_input);
336
194 if (fs->in) 337 if (fs->in)
195 mu_stream_destroy (&fs->in); 338 mu_stream_destroy (&fs->in);
196 if (fs->out) 339 if (fs->out)
...@@ -245,8 +388,6 @@ static int ...@@ -245,8 +388,6 @@ static int
245 feed_input (struct _mu_prog_stream *fs) 388 feed_input (struct _mu_prog_stream *fs)
246 { 389 {
247 pid_t pid; 390 pid_t pid;
248 size_t size;
249 char buffer[128];
250 int rc = 0; 391 int rc = 0;
251 392
252 pid = fork (); 393 pid = fork ();
...@@ -261,10 +402,7 @@ feed_input (struct _mu_prog_stream *fs) ...@@ -261,10 +402,7 @@ feed_input (struct _mu_prog_stream *fs)
261 402
262 case 0: 403 case 0:
263 /* Child */ 404 /* Child */
264 while (mu_stream_read (fs->input, buffer, sizeof (buffer), 405 mu_stream_copy (fs->out, fs->hints.mu_prog_input, 0, NULL);
265 &size) == 0
266 && size > 0)
267 mu_stream_write (fs->out, buffer, size, NULL);
268 mu_stream_close (fs->out); 406 mu_stream_close (fs->out);
269 exit (0); 407 exit (0);
270 408
...@@ -295,7 +433,7 @@ _prog_open (mu_stream_t stream) ...@@ -295,7 +433,7 @@ _prog_open (mu_stream_t stream)
295 mu_stream_get_flags (stream, &flags); 433 mu_stream_get_flags (stream, &flags);
296 seekable_flag = (flags & MU_STREAM_SEEK); 434 seekable_flag = (flags & MU_STREAM_SEEK);
297 435
298 rc = start_program_filter (&fs->pid, pfd, fs->argc, fs->argv, NULL, flags); 436 rc = start_program_filter (pfd, fs, flags);
299 if (rc) 437 if (rc)
300 return rc; 438 return rc;
301 439
...@@ -322,7 +460,7 @@ _prog_open (mu_stream_t stream) ...@@ -322,7 +460,7 @@ _prog_open (mu_stream_t stream)
322 } 460 }
323 461
324 _prog_stream_register (fs); 462 _prog_stream_register (fs);
325 if (fs->input) 463 if (fs->hint_flags & MU_PROG_HINT_INPUT)
326 return feed_input (fs); 464 return feed_input (fs);
327 return 0; 465 return 0;
328 } 466 }
...@@ -408,30 +546,25 @@ _prog_ioctl (struct _mu_stream *str, int code, int opcode, void *ptr) ...@@ -408,30 +546,25 @@ _prog_ioctl (struct _mu_stream *str, int code, int opcode, void *ptr)
408 return 0; 546 return 0;
409 } 547 }
410 548
411 struct _mu_prog_stream * 549 /* NOTE: Steals argv */
412 _prog_stream_create (const char *progname, int flags) 550 static struct _mu_prog_stream *
551 _prog_stream_create (const char *progname, size_t argc, char **argv,
552 int hint_flags, struct mu_prog_hints *hints, int flags)
413 { 553 {
414 struct _mu_prog_stream *fs; 554 struct _mu_prog_stream *fs;
415 struct mu_wordsplit ws;
416 555
417 fs = (struct _mu_prog_stream *) _mu_stream_create (sizeof (*fs), flags); 556 fs = (struct _mu_prog_stream *) _mu_stream_create (sizeof (*fs), flags);
418 if (!fs) 557 if (!fs)
419 return NULL; 558 return NULL;
420 559
421 ws.ws_comment = "#"; 560 fs->progname = strdup (progname);
422 if (mu_wordsplit (progname, &ws, MU_WRDSF_DEFFLAGS|MU_WRDSF_COMMENT)) 561 if (!fs->progname)
423 { 562 {
424 mu_error (_("cannot split line `%s': %s"), progname,
425 mu_wordsplit_strerror (&ws));
426 free (fs); 563 free (fs);
427 return NULL; 564 return NULL;
428 } 565 }
429 fs->argc = ws.ws_wordc; 566 fs->argc = argc;
430 fs->argv = ws.ws_wordv; 567 fs->argv = argv;
431 ws.ws_wordc = 0;
432 ws.ws_wordv = NULL;
433 mu_wordsplit_free (&ws);
434
435 fs->stream.read = _prog_read; 568 fs->stream.read = _prog_read;
436 fs->stream.write = _prog_write; 569 fs->stream.write = _prog_write;
437 fs->stream.open = _prog_open; 570 fs->stream.open = _prog_open;
...@@ -440,14 +573,67 @@ _prog_stream_create (const char *progname, int flags) ...@@ -440,14 +573,67 @@ _prog_stream_create (const char *progname, int flags)
440 fs->stream.flush = _prog_flush; 573 fs->stream.flush = _prog_flush;
441 fs->stream.done = _prog_done; 574 fs->stream.done = _prog_done;
442 575
576 if (!hints)
577 fs->hint_flags = 0;
578 else
579 {
580 fs->hint_flags = (hint_flags & _MU_PROG_HINT_MASK) |
581 (hint_flags & _MU_PROG_AVAILABLE_LIMITS);
582 if (fs->hint_flags & MU_PROG_HINT_WORKDIR)
583 {
584 fs->hints.mu_prog_workdir = strdup (hints->mu_prog_workdir);
585 if (!fs->hints.mu_prog_workdir)
586 {
587 free (fs);
588 return NULL;
589 }
590 }
591 memcpy (fs->hints.mu_prog_limit, hints->mu_prog_limit,
592 sizeof (fs->hints.mu_prog_limit));
593 fs->hints.mu_prog_prio = hints->mu_prog_prio;
594 if (fs->hint_flags & MU_PROG_HINT_INPUT)
595 {
596 fs->hints.mu_prog_input = hints->mu_prog_input;
597 mu_stream_ref (fs->hints.mu_prog_input);
598 }
599 if (fs->hint_flags & MU_PROG_HINT_UID)
600 {
601 fs->hints.mu_prog_uid = hints->mu_prog_uid;
602 if (fs->hint_flags & MU_PROG_HINT_GID)
603 {
604 fs->hints.mu_prog_gidv = calloc (hints->mu_prog_gidc,
605 sizeof (fs->hints.mu_prog_gidv[0]));
606 if (!fs->hints.mu_prog_gidv)
607 {
608 mu_stream_unref ((mu_stream_t) fs);
609 return NULL;
610 }
611 memcpy (fs->hints.mu_prog_gidv, hints->mu_prog_gidv,
612 hints->mu_prog_gidc * fs->hints.mu_prog_gidv[0]);
613 fs->hints.mu_prog_gidc = hints->mu_prog_gidc;
614 }
615 else
616 {
617 fs->hints.mu_prog_gidc = 0;
618 fs->hints.mu_prog_gidv = NULL;
619 }
620 }
621 }
622
443 return fs; 623 return fs;
444 } 624 }
445 625
446 int 626 int
447 mu_prog_stream_create (mu_stream_t *pstream, const char *progname, int flags) 627 mu_prog_stream_create (mu_stream_t *pstream,
628 const char *progname, size_t argc, char **argv,
629 int hint_flags,
630 struct mu_prog_hints *hints,
631 int flags)
448 { 632 {
449 int rc; 633 int rc;
450 mu_stream_t stream; 634 mu_stream_t stream;
635 char **xargv;
636 size_t i;
451 637
452 if (pstream == NULL) 638 if (pstream == NULL)
453 return MU_ERR_OUT_PTR_NULL; 639 return MU_ERR_OUT_PTR_NULL;
...@@ -455,8 +641,26 @@ mu_prog_stream_create (mu_stream_t *pstream, const char *progname, int flags) ...@@ -455,8 +641,26 @@ mu_prog_stream_create (mu_stream_t *pstream, const char *progname, int flags)
455 if (progname == NULL) 641 if (progname == NULL)
456 return EINVAL; 642 return EINVAL;
457 643
458 if ((stream = (mu_stream_t) _prog_stream_create (progname, flags)) == NULL) 644 xargv = calloc (argc + 1, sizeof (xargv[0]));
645 if (!xargv)
646 return ENOMEM;
647
648 for (i = 0; i < argc; i++)
649 {
650 xargv[i] = strdup (argv[i]);
651 if (!xargv[i])
652 {
653 mu_argcv_free (i, argv);
459 return ENOMEM; 654 return ENOMEM;
655 }
656 }
657 stream = (mu_stream_t) _prog_stream_create (progname, argc, xargv,
658 hint_flags, hints, flags);
659 if (!stream)
660 {
661 mu_argcv_free (argc, xargv);
662 return ENOMEM;
663 }
460 664
461 rc = mu_stream_open (stream); 665 rc = mu_stream_open (stream);
462 if (rc) 666 if (rc)
...@@ -467,30 +671,38 @@ mu_prog_stream_create (mu_stream_t *pstream, const char *progname, int flags) ...@@ -467,30 +671,38 @@ mu_prog_stream_create (mu_stream_t *pstream, const char *progname, int flags)
467 } 671 }
468 672
469 int 673 int
470 mu_filter_prog_stream_create (mu_stream_t *pstream, const char *progname, 674 mu_command_stream_create (mu_stream_t *pstream, const char *command,
471 mu_stream_t input) 675 int flags)
472 { 676 {
473 int rc; 677 int rc;
474 mu_stream_t stream; 678 mu_stream_t stream;
475 struct _mu_prog_stream *fs; 679 struct mu_wordsplit ws;
476 680
477 if (pstream == NULL) 681 if (pstream == NULL)
478 return MU_ERR_OUT_PTR_NULL; 682 return MU_ERR_OUT_PTR_NULL;
479 683
480 if (progname == NULL) 684 if (command == NULL)
481 return EINVAL; 685 return EINVAL;
482 686
483 fs = _prog_stream_create (progname, MU_STREAM_RDWR); 687 ws.ws_comment = "#";
484 if (!fs) 688 if (mu_wordsplit (command, &ws, MU_WRDSF_DEFFLAGS|MU_WRDSF_COMMENT))
485 return ENOMEM; 689 {
486 mu_stream_ref (input); 690 mu_error (_("cannot split line `%s': %s"), command,
487 fs->input = input; 691 mu_wordsplit_strerror (&ws));
488 stream = (mu_stream_t) fs; 692 return errno;
489 rc = mu_stream_open (stream); 693 }
490 if (rc) 694
491 mu_stream_destroy (&stream); 695 rc = mu_prog_stream_create (&stream,
492 else 696 ws.ws_wordv[0],
697 ws.ws_wordc, ws.ws_wordv,
698 0, NULL, flags);
699 if (rc == 0)
700 {
701 ws.ws_wordc = 0;
702 ws.ws_wordv = NULL;
493 *pstream = stream; 703 *pstream = stream;
704 }
705 mu_wordsplit_free (&ws);
706
494 return rc; 707 return rc;
495 } 708 }
496
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
39 #include <signal.h> 39 #include <signal.h>
40 #include <regex.h> 40 #include <regex.h>
41 #include <mailutils/sieve.h> 41 #include <mailutils/sieve.h>
42 #include <mailutils/prog.h>
42 43
43 int 44 int
44 sieve_action_pipe (mu_sieve_machine_t mach, mu_list_t args, mu_list_t tags) 45 sieve_action_pipe (mu_sieve_machine_t mach, mu_list_t args, mu_list_t tags)
...@@ -86,7 +87,7 @@ sieve_action_pipe (mu_sieve_machine_t mach, mu_list_t args, mu_list_t tags) ...@@ -86,7 +87,7 @@ sieve_action_pipe (mu_sieve_machine_t mach, mu_list_t args, mu_list_t tags)
86 rc = mu_message_get_streamref (msg, &mstr); 87 rc = mu_message_get_streamref (msg, &mstr);
87 ONERR (rc, _("cannot get message stream"), NULL); 88 ONERR (rc, _("cannot get message stream"), NULL);
88 89
89 rc = mu_prog_stream_create (&pstr, cmd, MU_STREAM_WRITE); 90 rc = mu_command_stream_create (&pstr, cmd, MU_STREAM_WRITE);
90 ONERR (rc, _("cannot create command stream"), cmd); 91 ONERR (rc, _("cannot create command stream"), cmd);
91 92
92 if (mu_sieve_tag_lookup (tags, "envelope", &val)) 93 if (mu_sieve_tag_lookup (tags, "envelope", &val))
......
...@@ -409,10 +409,11 @@ run_metamail (const char *mailcap_cmd, mu_message_t mesg) ...@@ -409,10 +409,11 @@ run_metamail (const char *mailcap_cmd, mu_message_t mesg)
409 break; 409 break;
410 } 410 }
411 411
412 status = mu_prog_stream_create (&pstr, mailcap_cmd, MU_STREAM_WRITE); 412 status = mu_command_stream_create (&pstr, mailcap_cmd,
413 MU_STREAM_WRITE);
413 if (status) 414 if (status)
414 { 415 {
415 mu_error ("mu_prog_stream_create: %s", mu_strerror (status)); 416 mu_error ("mu_command_stream_create: %s", mu_strerror (status));
416 break; 417 break;
417 } 418 }
418 419
......
...@@ -82,6 +82,7 @@ ...@@ -82,6 +82,7 @@
82 #include <mailutils/cstr.h> 82 #include <mailutils/cstr.h>
83 #include <mailutils/io.h> 83 #include <mailutils/io.h>
84 #include <mailutils/stdstream.h> 84 #include <mailutils/stdstream.h>
85 #include <mailutils/prog.h>
85 86
86 #ifdef __cplusplus 87 #ifdef __cplusplus
87 extern "C" { 88 extern "C" {
......
...@@ -38,7 +38,7 @@ mail_pipe (int argc, char **argv) ...@@ -38,7 +38,7 @@ mail_pipe (int argc, char **argv)
38 if (msgset_parse (argc, argv, MSG_NODELETED|MSG_SILENT, &list)) 38 if (msgset_parse (argc, argv, MSG_NODELETED|MSG_SILENT, &list))
39 return 1; 39 return 1;
40 40
41 rc = mu_prog_stream_create (&outstr, cmd, MU_STREAM_WRITE); 41 rc = mu_command_stream_create (&outstr, cmd, MU_STREAM_WRITE);
42 if (rc) 42 if (rc)
43 { 43 {
44 mu_error (_("cannot open `%s': %s"), cmd, mu_strerror (rc)); 44 mu_error (_("cannot open `%s': %s"), cmd, mu_strerror (rc));
......
...@@ -702,7 +702,7 @@ msg_to_pipe (const char *cmd, mu_message_t msg) ...@@ -702,7 +702,7 @@ msg_to_pipe (const char *cmd, mu_message_t msg)
702 mu_stream_t progstream, msgstream; 702 mu_stream_t progstream, msgstream;
703 int status, rc; 703 int status, rc;
704 704
705 status = mu_prog_stream_create (&progstream, cmd, MU_STREAM_WRITE); 705 status = mu_command_stream_create (&progstream, cmd, MU_STREAM_WRITE);
706 if (status) 706 if (status)
707 { 707 {
708 util_error (_("Cannot pipe to %s: %s"), cmd, mu_strerror (status)); 708 util_error (_("Cannot pipe to %s: %s"), cmd, mu_strerror (status));
......
...@@ -56,6 +56,7 @@ ...@@ -56,6 +56,7 @@
56 #include <mailutils/mime.h> 56 #include <mailutils/mime.h>
57 #include <mailutils/io.h> 57 #include <mailutils/io.h>
58 #include <mailutils/property.h> 58 #include <mailutils/property.h>
59 #include <mailutils/prog.h>
59 #include <mailutils/mh.h> 60 #include <mailutils/mh.h>
60 61
61 #include <mu_umaxtostr.h> 62 #include <mu_umaxtostr.h>
......
...@@ -149,7 +149,7 @@ open_output () ...@@ -149,7 +149,7 @@ open_output ()
149 moreproc = NULL; 149 moreproc = NULL;
150 150
151 if (moreproc) 151 if (moreproc)
152 rc = mu_prog_stream_create (&output, moreproc, MU_STREAM_WRITE); 152 rc = mu_command_stream_create (&output, moreproc, MU_STREAM_WRITE);
153 else 153 else
154 rc = mu_stdio_stream_create (&output, MU_STDOUT_FD, MU_STREAM_WRITE); 154 rc = mu_stdio_stream_create (&output, MU_STDOUT_FD, MU_STREAM_WRITE);
155 155
......
...@@ -1403,7 +1403,7 @@ show_internal (mu_message_t msg, msg_part_t part, char *encoding, ...@@ -1403,7 +1403,7 @@ show_internal (mu_message_t msg, msg_part_t part, char *encoding,
1403 int 1403 int
1404 mhn_exec (mu_stream_t *str, const char *cmd, int flags) 1404 mhn_exec (mu_stream_t *str, const char *cmd, int flags)
1405 { 1405 {
1406 int rc = mu_prog_stream_create (str, cmd, MU_STREAM_WRITE); 1406 int rc = mu_command_stream_create (str, cmd, MU_STREAM_WRITE);
1407 if (rc) 1407 if (rc)
1408 { 1408 {
1409 mu_error (_("cannot create proc stream (command %s): %s"), 1409 mu_error (_("cannot create proc stream (command %s): %s"),
...@@ -1773,16 +1773,30 @@ store_handler (mu_message_t msg, msg_part_t part, char *type, char *encoding, ...@@ -1773,16 +1773,30 @@ store_handler (mu_message_t msg, msg_part_t part, char *type, char *encoding,
1773 break; 1773 break;
1774 1774
1775 case store_to_command: 1775 case store_to_command:
1776 /* FIXME: Change to homedir, reflect this in the message below. 1776 {
1777 Chdir should better be implemented within mu_prog_stream_create 1777 struct mu_prog_hints hints;
1778 Example message: 1778 struct mu_wordsplit ws;
1779 storing msg 4 part 1 using command (cd /home/gray; less) 1779
1780 */ 1780 hints.mu_prog_workdir = mu_get_homedir ();
1781 printf (_("storing msg %s part %s using command %s\n"), 1781 ws.ws_comment = "#";
1782 prefix, partstr, name); 1782 if (mu_wordsplit (name, &ws, MU_WRDSF_DEFFLAGS|MU_WRDSF_COMMENT))
1783 rc = mu_prog_stream_create (&out, name, MU_STREAM_WRITE); 1783 {
1784 mu_error (_("cannot split line `%s': %s"), name,
1785 mu_wordsplit_strerror (&ws));
1786 break;
1787 }
1788
1789 printf (_("storing msg %s part %s using command (cd %s; %s)\n"),
1790 prefix, partstr, hints.mu_prog_workdir, name);
1791 rc = mu_prog_stream_create (&out,
1792 ws.ws_wordv[0],
1793 ws.ws_wordc, ws.ws_wordv,
1794 MU_PROG_HINT_WORKDIR,
1795 &hints, MU_STREAM_WRITE);
1796 mu_wordsplit_free (&ws);
1784 if (rc) 1797 if (rc)
1785 mu_diag_funcall (MU_DIAG_ERROR, "mu_prog_stream_create", NULL, rc); 1798 mu_diag_funcall (MU_DIAG_ERROR, "mu_prog_stream_create", NULL, rc);
1799 }
1786 break; 1800 break;
1787 1801
1788 case store_to_stdout: 1802 case store_to_stdout:
......
...@@ -247,7 +247,7 @@ mkdir Mail/inbox ...@@ -247,7 +247,7 @@ mkdir Mail/inbox
247 cp $abs_top_srcdir/testsuite/mh/mbox1/4 Mail/inbox 247 cp $abs_top_srcdir/testsuite/mh/mbox1/4 Mail/inbox
248 echo "Current-Folder: inbox" > Mail/context 248 echo "Current-Folder: inbox" > Mail/context
249 echo "mhn-store-text: | $abs_top_srcdir/mh/tests/mhed -" >> $MH 249 echo "mhn-store-text: | $abs_top_srcdir/mh/tests/mhed -" >> $MH
250 mhn +inbox -store -part 1 4 | sed "s|$abs_top_srcdir/mh/tests/||;s| *$||" || exit $? 250 mhn +inbox -store -part 1 4 | sed "s|$abs_top_srcdir/mh/tests/||;s|(cd \(.*\)\;|(cd home\;|;s| *$||" || exit $?
251 ], 251 ],
252 [0], 252 [0],
253 [-- Editor invocation: - 253 [-- Editor invocation: -
...@@ -263,7 +263,7 @@ But, now that I'm perfectly sure I have none, ...@@ -263,7 +263,7 @@ But, now that I'm perfectly sure I have none,
263 Why, I do it again and again.' 263 Why, I do it again and again.'
264 264
265 -- Input file end 265 -- Input file end
266 storing msg 4 part 1 using command mhed - 266 storing msg 4 part 1 using command (cd home; mhed -)
267 ]) 267 ])
268 268
269 dnl ------------------------------------------------------------------- 269 dnl -------------------------------------------------------------------
......
...@@ -240,7 +240,7 @@ mutool_open_pager () ...@@ -240,7 +240,7 @@ mutool_open_pager ()
240 if (mutool_shell_interactive && (pager = getenv ("PAGER")) != NULL) 240 if (mutool_shell_interactive && (pager = getenv ("PAGER")) != NULL)
241 { 241 {
242 mu_stream_t stream; 242 mu_stream_t stream;
243 int rc = mu_prog_stream_create (&stream, pager, MU_STREAM_WRITE); 243 int rc = mu_command_stream_create (&stream, pager, MU_STREAM_WRITE);
244 if (rc == 0) 244 if (rc == 0)
245 return stream; 245 return stream;
246 mu_error (_("cannot start pager: %s"), mu_strerror (rc)); 246 mu_error (_("cannot start pager: %s"), mu_strerror (rc));
......
...@@ -118,6 +118,7 @@ mail/unalias.c ...@@ -118,6 +118,7 @@ mail/unalias.c
118 mail/util.c 118 mail/util.c
119 mail/z.c 119 mail/z.c
120 120
121 libmailutils/base/userprivs.c
121 libmailutils/cfg/driver.c 122 libmailutils/cfg/driver.c
122 libmailutils/cfg/format.c 123 libmailutils/cfg/format.c
123 libmailutils/cfg/lexer.l 124 libmailutils/cfg/lexer.l
......