4cbbd06970f8e754d6ebb6c896e128cf217e76db
[multipath-tools/.git] / libmultipath / callout.c
1 /*
2  * Source: copy of the udev package source file
3  *
4  * Copyrights of the source file apply
5  * Copyright (c) 2004 Christophe Varoqui
6  */
7 #include <stdio.h>
8 #include <sys/stat.h>
9 #include <string.h>
10 #include <unistd.h>
11 #include <sys/types.h>
12 #include <stdlib.h>
13 #include <fcntl.h>
14 #include <sys/wait.h>
15 #include <errno.h>
16
17 #include "checkers.h"
18 #include "vector.h"
19 #include "structs.h"
20 #include "util.h"
21 #include "debug.h"
22
23 int execute_program(char *path, char *value, int len)
24 {
25         int retval;
26         int count;
27         int status;
28         int fds[2], null_fd;
29         pid_t pid;
30         char *pos;
31         char arg[CALLOUT_MAX_SIZE];
32         int argc = sizeof(arg) / 2;
33         char *argv[argc + 1];
34         int i;
35
36         i = 0;
37
38         if (strchr(path, ' ')) {
39                 strlcpy(arg, path, sizeof(arg));
40                 pos = arg;
41                 while (pos != NULL && i < argc) {
42                         if (pos[0] == '\'') {
43                                 /* don't separate if in apostrophes */
44                                 pos++;
45                                 argv[i] = strsep(&pos, "\'");
46                                 while (pos[0] == ' ')
47                                         pos++;
48                         } else {
49                                 argv[i] = strsep(&pos, " ");
50                         }
51                         i++;
52                 }
53         } else {
54                 argv[i++] = path;
55         }
56         argv[i] =  NULL;
57
58         retval = pipe(fds);
59
60         if (retval != 0) {
61                 condlog(0, "error creating pipe for callout: %s", strerror(errno));
62                 return -1;
63         }
64
65         pid = fork();
66
67         switch(pid) {
68         case 0:
69                 /* child */
70                 close(STDOUT_FILENO);
71
72                 /* dup write side of pipe to STDOUT */
73                 if (dup(fds[1]) < 0)
74                         return -1;
75
76                 /* Ignore writes to stderr */
77                 null_fd = open("/dev/null", O_WRONLY);
78                 if (null_fd > 0) {
79                         close(STDERR_FILENO);
80                         retval = dup(null_fd);
81                         close(null_fd);
82                 }
83
84                 retval = execv(argv[0], argv);
85                 condlog(0, "error execing %s : %s", argv[0], strerror(errno));
86                 exit(-1);
87         case -1:
88                 condlog(0, "fork failed: %s", strerror(errno));
89                 close(fds[0]);
90                 close(fds[1]);
91                 return -1;
92         default:
93                 /* parent reads from fds[0] */
94                 close(fds[1]);
95                 retval = 0;
96                 i = 0;
97                 while (1) {
98                         count = read(fds[0], value + i, len - i-1);
99                         if (count <= 0)
100                                 break;
101
102                         i += count;
103                         if (i >= len-1) {
104                                 condlog(0, "not enough space for response from %s", argv[0]);
105                                 retval = -1;
106                                 break;
107                         }
108                 }
109
110                 if (count < 0) {
111                         condlog(0, "no response from %s", argv[0]);
112                         retval = -1;
113                 }
114
115                 if (i > 0 && value[i-1] == '\n')
116                         i--;
117                 value[i] = '\0';
118
119                 wait(&status);
120                 close(fds[0]);
121
122                 retval = -1;
123                 if (WIFEXITED(status)) {
124                         status = WEXITSTATUS(status);
125                         if (status == 0)
126                                 retval = 0;
127                         else
128                                 condlog(0, "%s exited with %d", argv[0], status);
129                 }
130                 else if (WIFSIGNALED(status))
131                         condlog(0, "%s was terminated by signal %d", argv[0], WTERMSIG(status));
132                 else
133                         condlog(0, "%s terminated abnormally", argv[0]);
134         }
135         return retval;
136 }
137
138 extern int
139 apply_format (char * string, char * cmd, struct path * pp)
140 {
141         char * pos;
142         char * dst;
143         char * p;
144         char * q;
145         int len;
146         int myfree;
147
148         if (!string)
149                 return 1;
150
151         if (!cmd)
152                 return 1;
153
154         dst = cmd;
155         p = dst;
156         pos = strchr(string, '%');
157         myfree = CALLOUT_MAX_SIZE;
158
159         if (!pos) {
160                 strcpy(dst, string);
161                 return 0;
162         }
163
164         len = (int) (pos - string) + 1;
165         myfree -= len;
166
167         if (myfree < 2)
168                 return 1;
169
170         snprintf(p, len, "%s", string);
171         p += len - 1;
172         pos++;
173
174         switch (*pos) {
175         case 'n':
176                 len = strlen(pp->dev) + 1;
177                 myfree -= len;
178
179                 if (myfree < 2)
180                         return 1;
181
182                 snprintf(p, len, "%s", pp->dev);
183                 for (q = p; q < p + len; q++) {
184                         if (q && *q == '!')
185                                 *q = '/';
186                 }
187                 p += len - 1;
188                 break;
189         case 'd':
190                 len = strlen(pp->dev_t) + 1;
191                 myfree -= len;
192
193                 if (myfree < 2)
194                         return 1;
195
196                 snprintf(p, len, "%s", pp->dev_t);
197                 p += len - 1;
198                 break;
199         default:
200                 break;
201         }
202         pos++;
203
204         if (!*pos)
205                 return 0;
206
207         len = strlen(pos) + 1;
208         myfree -= len;
209
210         if (myfree < 2)
211                 return 1;
212
213         snprintf(p, len, "%s", pos);
214         condlog(3, "reformated callout = %s", dst);
215         return 0;
216 }