]>
Commit | Line | Data |
---|---|---|
59d6bfef | 1 | /* |
59d6bfef KS |
2 | * Copyright (C) 2004-2005 Kay Sievers <kay.sievers@vrfy.org> |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify it | |
5 | * under the terms of the GNU General Public License as published by the | |
6 | * Free Software Foundation version 2 of the License. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, but | |
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
11 | * General Public License for more details. | |
12 | * | |
13 | * You should have received a copy of the GNU General Public License along | |
14 | * with this program; if not, write to the Free Software Foundation, Inc., | |
27b77df4 | 15 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
59d6bfef KS |
16 | * |
17 | */ | |
18 | ||
19 | ||
20 | #include <stdlib.h> | |
21 | #include <stdio.h> | |
22 | #include <stddef.h> | |
23 | #include <unistd.h> | |
24 | #include <fcntl.h> | |
25 | #include <errno.h> | |
26 | #include <ctype.h> | |
fb819f55 | 27 | #include <syslog.h> |
59d6bfef KS |
28 | #include <sys/socket.h> |
29 | #include <sys/un.h> | |
30 | #include <sys/wait.h> | |
27f877e6 | 31 | #include <sys/select.h> |
59d6bfef | 32 | |
59d6bfef | 33 | #include "udev.h" |
59d6bfef | 34 | |
aaa14841 | 35 | extern char **environ; |
59d6bfef KS |
36 | |
37 | int pass_env_to_socket(const char *sockname, const char *devpath, const char *action) | |
38 | { | |
39 | int sock; | |
40 | struct sockaddr_un saddr; | |
41 | socklen_t addrlen; | |
42 | char buf[2048]; | |
43 | size_t bufpos = 0; | |
44 | int i; | |
40caaeec KS |
45 | ssize_t count; |
46 | int retval = 0; | |
59d6bfef KS |
47 | |
48 | dbg("pass environment to socket '%s'", sockname); | |
49 | sock = socket(AF_LOCAL, SOCK_DGRAM, 0); | |
50 | memset(&saddr, 0x00, sizeof(struct sockaddr_un)); | |
51 | saddr.sun_family = AF_LOCAL; | |
d8a57e7c | 52 | /* abstract namespace only */ |
59d6bfef KS |
53 | strcpy(&saddr.sun_path[1], sockname); |
54 | addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(saddr.sun_path+1) + 1; | |
55 | ||
56 | bufpos = snprintf(buf, sizeof(buf)-1, "%s@%s", action, devpath); | |
57 | bufpos++; | |
58 | for (i = 0; environ[i] != NULL && bufpos < sizeof(buf); i++) { | |
59 | bufpos += strlcpy(&buf[bufpos], environ[i], sizeof(buf) - bufpos-1); | |
60 | bufpos++; | |
61 | } | |
62 | ||
40caaeec KS |
63 | count = sendto(sock, &buf, bufpos, 0, (struct sockaddr *)&saddr, addrlen); |
64 | if (count < 0) | |
65 | retval = -1; | |
66 | info("passed %zi bytes to socket '%s', ", count, sockname); | |
59d6bfef KS |
67 | |
68 | close(sock); | |
69 | return retval; | |
70 | } | |
71 | ||
27f877e6 | 72 | int run_program(const char *command, const char *subsystem, |
fb819f55 | 73 | char *result, size_t ressize, size_t *reslen) |
59d6bfef | 74 | { |
59d6bfef | 75 | int status; |
27f877e6 KS |
76 | int outpipe[2] = {-1, -1}; |
77 | int errpipe[2] = {-1, -1}; | |
59d6bfef | 78 | pid_t pid; |
59d6bfef | 79 | char arg[PATH_SIZE]; |
aab4c0ee | 80 | char program[PATH_SIZE]; |
59d6bfef KS |
81 | char *argv[(sizeof(arg) / 2) + 1]; |
82 | int devnull; | |
83 | int i; | |
fb819f55 | 84 | int retval = 0; |
59d6bfef | 85 | |
36af2ddc | 86 | /* build argv from comand */ |
59d6bfef KS |
87 | strlcpy(arg, command, sizeof(arg)); |
88 | i = 0; | |
36af2ddc | 89 | if (strchr(arg, ' ') != NULL) { |
40caaeec | 90 | char *pos = arg; |
36af2ddc | 91 | |
59d6bfef KS |
92 | while (pos != NULL) { |
93 | if (pos[0] == '\'') { | |
94 | /* don't separate if in apostrophes */ | |
95 | pos++; | |
96 | argv[i] = strsep(&pos, "\'"); | |
36af2ddc | 97 | while (pos != NULL && pos[0] == ' ') |
59d6bfef KS |
98 | pos++; |
99 | } else { | |
100 | argv[i] = strsep(&pos, " "); | |
101 | } | |
102 | dbg("arg[%i] '%s'", i, argv[i]); | |
103 | i++; | |
104 | } | |
27f877e6 | 105 | argv[i] = NULL; |
59d6bfef KS |
106 | } else { |
107 | argv[0] = arg; | |
36af2ddc | 108 | argv[1] = NULL; |
59d6bfef | 109 | } |
36af2ddc | 110 | info("'%s'", command); |
59d6bfef | 111 | |
27f877e6 | 112 | /* prepare pipes from child to parent */ |
fb819f55 | 113 | if (result != NULL || udev_log_priority >= LOG_INFO) { |
27f877e6 | 114 | if (pipe(outpipe) != 0) { |
ff3e4bed | 115 | err("pipe failed: %s", strerror(errno)); |
27f877e6 KS |
116 | return -1; |
117 | } | |
118 | } | |
fb819f55 | 119 | if (udev_log_priority >= LOG_INFO) { |
27f877e6 | 120 | if (pipe(errpipe) != 0) { |
ff3e4bed | 121 | err("pipe failed: %s", strerror(errno)); |
59d6bfef KS |
122 | return -1; |
123 | } | |
124 | } | |
125 | ||
aab4c0ee KS |
126 | /* allow programs in /lib/udev called without the path */ |
127 | if (strchr(argv[0], '/') == NULL) { | |
128 | strlcpy(program, "/lib/udev/", sizeof(program)); | |
129 | strlcat(program, argv[0], sizeof(program)); | |
130 | argv[0] = program; | |
131 | } | |
132 | ||
59d6bfef KS |
133 | pid = fork(); |
134 | switch(pid) { | |
135 | case 0: | |
27f877e6 | 136 | /* child closes parent ends of pipes */ |
f1ff8d7b KS |
137 | if (outpipe[READ_END] > 0) |
138 | close(outpipe[READ_END]); | |
139 | if (errpipe[READ_END] > 0) | |
140 | close(errpipe[READ_END]); | |
27f877e6 KS |
141 | |
142 | /* discard child output or connect to pipe */ | |
59d6bfef | 143 | devnull = open("/dev/null", O_RDWR); |
af5461f7 KS |
144 | if (devnull > 0) { |
145 | dup2(devnull, STDIN_FILENO); | |
f1ff8d7b | 146 | if (outpipe[WRITE_END] < 0) |
af5461f7 | 147 | dup2(devnull, STDOUT_FILENO); |
f1ff8d7b | 148 | if (errpipe[WRITE_END] < 0) |
af5461f7 KS |
149 | dup2(devnull, STDERR_FILENO); |
150 | close(devnull); | |
151 | } else | |
ff3e4bed | 152 | err("open /dev/null failed: %s", strerror(errno)); |
b83b2991 | 153 | if (outpipe[WRITE_END] > 0) { |
f1ff8d7b | 154 | dup2(outpipe[WRITE_END], STDOUT_FILENO); |
b83b2991 MI |
155 | close(outpipe[WRITE_END]); |
156 | } | |
157 | if (errpipe[WRITE_END] > 0) { | |
f1ff8d7b | 158 | dup2(errpipe[WRITE_END], STDERR_FILENO); |
b83b2991 MI |
159 | close(errpipe[WRITE_END]); |
160 | } | |
40caaeec | 161 | execv(argv[0], argv); |
9b2e2d4a | 162 | if (errno == ENOENT || errno == ENOTDIR) { |
8246d00d SJR |
163 | /* may be on a filesytem which is not mounted right now */ |
164 | info("program '%s' not found", argv[0]); | |
165 | } else { | |
166 | /* other problems */ | |
167 | err("exec of program '%s' failed", argv[0]); | |
168 | } | |
59d6bfef KS |
169 | _exit(1); |
170 | case -1: | |
ff3e4bed | 171 | err("fork of '%s' failed: %s", argv[0], strerror(errno)); |
59d6bfef KS |
172 | return -1; |
173 | default: | |
27f877e6 | 174 | /* read from child if requested */ |
f1ff8d7b | 175 | if (outpipe[READ_END] > 0 || errpipe[READ_END] > 0) { |
853ccc43 | 176 | ssize_t count; |
27f877e6 KS |
177 | size_t respos = 0; |
178 | ||
179 | /* parent closes child ends of pipes */ | |
f1ff8d7b KS |
180 | if (outpipe[WRITE_END] > 0) |
181 | close(outpipe[WRITE_END]); | |
182 | if (errpipe[WRITE_END] > 0) | |
183 | close(errpipe[WRITE_END]); | |
27f877e6 KS |
184 | |
185 | /* read child output */ | |
f1ff8d7b | 186 | while (outpipe[READ_END] > 0 || errpipe[READ_END] > 0) { |
27f877e6 KS |
187 | int fdcount; |
188 | fd_set readfds; | |
189 | ||
190 | FD_ZERO(&readfds); | |
f1ff8d7b KS |
191 | if (outpipe[READ_END] > 0) |
192 | FD_SET(outpipe[READ_END], &readfds); | |
193 | if (errpipe[READ_END] > 0) | |
194 | FD_SET(errpipe[READ_END], &readfds); | |
195 | fdcount = select(UDEV_MAX(outpipe[READ_END], errpipe[READ_END])+1, &readfds, NULL, NULL, NULL); | |
27f877e6 KS |
196 | if (fdcount < 0) { |
197 | if (errno == EINTR) | |
198 | continue; | |
59d6bfef KS |
199 | retval = -1; |
200 | break; | |
201 | } | |
202 | ||
27f877e6 | 203 | /* get stdout */ |
f1ff8d7b | 204 | if (outpipe[READ_END] > 0 && FD_ISSET(outpipe[READ_END], &readfds)) { |
27f877e6 | 205 | char inbuf[1024]; |
40caaeec KS |
206 | char *pos; |
207 | char *line; | |
59d6bfef | 208 | |
f1ff8d7b | 209 | count = read(outpipe[READ_END], inbuf, sizeof(inbuf)-1); |
27f877e6 | 210 | if (count <= 0) { |
f1ff8d7b KS |
211 | close(outpipe[READ_END]); |
212 | outpipe[READ_END] = -1; | |
27f877e6 | 213 | if (count < 0) { |
ff3e4bed | 214 | err("stdin read failed: %s", strerror(errno)); |
27f877e6 KS |
215 | retval = -1; |
216 | } | |
217 | continue; | |
218 | } | |
219 | inbuf[count] = '\0'; | |
27f877e6 | 220 | |
40caaeec | 221 | /* store result for rule processing */ |
27f877e6 | 222 | if (result) { |
40caaeec KS |
223 | if (respos + count < ressize) { |
224 | memcpy(&result[respos], inbuf, count); | |
225 | respos += count; | |
226 | } else { | |
27f877e6 KS |
227 | err("ressize %ld too short", (long)ressize); |
228 | retval = -1; | |
27f877e6 | 229 | } |
27f877e6 | 230 | } |
40caaeec KS |
231 | pos = inbuf; |
232 | while ((line = strsep(&pos, "\n"))) | |
233 | if (pos || line[0] != '\0') | |
234 | info("'%s' (stdout) '%s'", argv[0], line); | |
27f877e6 KS |
235 | } |
236 | ||
237 | /* get stderr */ | |
f1ff8d7b | 238 | if (errpipe[READ_END] > 0 && FD_ISSET(errpipe[READ_END], &readfds)) { |
27f877e6 | 239 | char errbuf[1024]; |
40caaeec KS |
240 | char *pos; |
241 | char *line; | |
27f877e6 | 242 | |
f1ff8d7b | 243 | count = read(errpipe[READ_END], errbuf, sizeof(errbuf)-1); |
27f877e6 | 244 | if (count <= 0) { |
f1ff8d7b KS |
245 | close(errpipe[READ_END]); |
246 | errpipe[READ_END] = -1; | |
27f877e6 | 247 | if (count < 0) |
ff3e4bed | 248 | err("stderr read failed: %s", strerror(errno)); |
27f877e6 KS |
249 | continue; |
250 | } | |
251 | errbuf[count] = '\0'; | |
40caaeec KS |
252 | pos = errbuf; |
253 | while ((line = strsep(&pos, "\n"))) | |
254 | if (pos || line[0] != '\0') | |
255 | info("'%s' (stderr) '%s'", argv[0], line); | |
59d6bfef KS |
256 | } |
257 | } | |
f1ff8d7b KS |
258 | if (outpipe[READ_END] > 0) |
259 | close(outpipe[READ_END]); | |
260 | if (errpipe[READ_END] > 0) | |
261 | close(errpipe[READ_END]); | |
27f877e6 KS |
262 | |
263 | /* return the childs stdout string */ | |
264 | if (result) { | |
265 | result[respos] = '\0'; | |
266 | dbg("result='%s'", result); | |
267 | if (reslen) | |
268 | *reslen = respos; | |
269 | } | |
59d6bfef KS |
270 | } |
271 | waitpid(pid, &status, 0); | |
40caaeec KS |
272 | if (WIFEXITED(status)) { |
273 | info("'%s' returned with status %i", argv[0], WEXITSTATUS(status)); | |
274 | if (WEXITSTATUS(status) != 0) | |
275 | retval = -1; | |
276 | } else { | |
277 | err("'%s' abnormal exit", argv[0]); | |
59d6bfef KS |
278 | retval = -1; |
279 | } | |
280 | } | |
281 | ||
282 | return retval; | |
283 | } |