chiark / gitweb /
c2e77cbbcc13c75e0ca1a96572f91291ce50199b
[elogind.git] / udev_utils_run.c
1 /*
2  * udev_utils_run.c - execute programs from udev and read its output
3  *
4  * Copyright (C) 2004-2005 Kay Sievers <kay.sievers@vrfy.org>
5  *
6  *      This program is free software; you can redistribute it and/or modify it
7  *      under the terms of the GNU General Public License as published by the
8  *      Free Software Foundation version 2 of the License.
9  * 
10  *      This program is distributed in the hope that it will be useful, but
11  *      WITHOUT ANY WARRANTY; without even the implied warranty of
12  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *      General Public License for more details.
14  * 
15  *      You should have received a copy of the GNU General Public License along
16  *      with this program; if not, write to the Free Software Foundation, Inc.,
17  *      675 Mass Ave, Cambridge, MA 02139, USA.
18  *
19  */
20
21
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <stddef.h>
25 #include <unistd.h>
26 #include <fcntl.h>
27 #include <errno.h>
28 #include <ctype.h>
29 #include <sys/socket.h>
30 #include <sys/un.h>
31 #include <sys/wait.h>
32
33 #include "udev_libc_wrapper.h"
34 #include "udev.h"
35 #include "logging.h"
36 #include "udev_utils.h"
37 #include "list.h"
38
39
40 int pass_env_to_socket(const char *sockname, const char *devpath, const char *action)
41 {
42         int sock;
43         struct sockaddr_un saddr;
44         socklen_t addrlen;
45         char buf[2048];
46         size_t bufpos = 0;
47         int i;
48         int retval;
49
50         dbg("pass environment to socket '%s'", sockname);
51         sock = socket(AF_LOCAL, SOCK_DGRAM, 0);
52         memset(&saddr, 0x00, sizeof(struct sockaddr_un));
53         saddr.sun_family = AF_LOCAL;
54         /* only abstract namespace is supported */
55         strcpy(&saddr.sun_path[1], sockname);
56         addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(saddr.sun_path+1) + 1;
57
58         bufpos = snprintf(buf, sizeof(buf)-1, "%s@%s", action, devpath);
59         bufpos++;
60         for (i = 0; environ[i] != NULL && bufpos < sizeof(buf); i++) {
61                 bufpos += strlcpy(&buf[bufpos], environ[i], sizeof(buf) - bufpos-1);
62                 bufpos++;
63         }
64
65         retval = sendto(sock, &buf, bufpos, 0, (struct sockaddr *)&saddr, addrlen);
66         if (retval != -1)
67                 retval = 0;
68
69         close(sock);
70         return retval;
71 }
72
73 int execute_program(const char *command, const char *subsystem,
74                     char *result, size_t ressize, size_t *reslen)
75 {
76         int retval = 0;
77         int count;
78         int status;
79         int pipefds[2];
80         pid_t pid;
81         char *pos;
82         char arg[PATH_SIZE];
83         char *argv[(sizeof(arg) / 2) + 1];
84         int devnull;
85         int i;
86         size_t len;
87
88         strlcpy(arg, command, sizeof(arg));
89         i = 0;
90         if (strchr(arg, ' ')) {
91                 pos = arg;
92                 while (pos != NULL) {
93                         if (pos[0] == '\'') {
94                                 /* don't separate if in apostrophes */
95                                 pos++;
96                                 argv[i] = strsep(&pos, "\'");
97                                 while (pos && pos[0] == ' ')
98                                         pos++;
99                         } else {
100                                 argv[i] = strsep(&pos, " ");
101                         }
102                         dbg("arg[%i] '%s'", i, argv[i]);
103                         i++;
104                 }
105                 argv[i] =  NULL;
106                 dbg("execute '%s' with parsed arguments", arg);
107         } else {
108                 argv[0] = arg;
109                 argv[1] = (char *) subsystem;
110                 argv[2] = NULL;
111                 dbg("execute '%s' with subsystem '%s' argument", arg, argv[1]);
112         }
113
114         if (result) {
115                 if (pipe(pipefds) != 0) {
116                         err("pipe failed");
117                         return -1;
118                 }
119         }
120
121         pid = fork();
122         switch(pid) {
123         case 0:
124                 /* child dup2 write side of pipe to STDOUT */
125                 devnull = open("/dev/null", O_RDWR);
126                 if (devnull >= 0) {
127                         dup2(devnull, STDIN_FILENO);
128                         if (!result)
129                                 dup2(devnull, STDOUT_FILENO);
130                         dup2(devnull, STDERR_FILENO);
131                         close(devnull);
132                 }
133                 if (result)
134                         dup2(pipefds[1], STDOUT_FILENO);
135                 execv(arg, argv);
136                 err("exec of program failed");
137                 _exit(1);
138         case -1:
139                 err("fork of '%s' failed", arg);
140                 return -1;
141         default:
142                 /* parent reads from pipefds[0] */
143                 if (result) {
144                         close(pipefds[1]);
145                         len = 0;
146                         while (1) {
147                                 count = read(pipefds[0], result + len, ressize - len-1);
148                                 if (count < 0) {
149                                         err("read failed with '%s'", strerror(errno));
150                                         retval = -1;
151                                         break;
152                                 }
153
154                                 if (count == 0)
155                                         break;
156
157                                 len += count;
158                                 if (len >= ressize-1) {
159                                         err("ressize %ld too short", (long)ressize);
160                                         retval = -1;
161                                         break;
162                                 }
163                         }
164                         result[len] = '\0';
165                         close(pipefds[0]);
166                         if (reslen)
167                                 *reslen = len;
168                 }
169                 waitpid(pid, &status, 0);
170
171                 if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0)) {
172                         dbg("exec program status 0x%x", status);
173                         retval = -1;
174                 }
175         }
176
177         return retval;
178 }
179