multipath-tools: add alias_prefix to multipath.conf.5
[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                         int err_fd __attribute__ ((unused));
80
81                         close(STDERR_FILENO);
82                         err_fd = dup(null_fd);
83                         close(null_fd);
84                 }
85
86                 retval = execv(argv[0], argv);
87                 condlog(0, "error execing %s : %s", argv[0], strerror(errno));
88                 exit(-1);
89         case -1:
90                 condlog(0, "fork failed: %s", strerror(errno));
91                 close(fds[0]);
92                 close(fds[1]);
93                 return -1;
94         default:
95                 /* parent reads from fds[0] */
96                 close(fds[1]);
97                 retval = 0;
98                 i = 0;
99                 while (1) {
100                         count = read(fds[0], value + i, len - i-1);
101                         if (count <= 0)
102                                 break;
103
104                         i += count;
105                         if (i >= len-1) {
106                                 condlog(0, "not enough space for response from %s", argv[0]);
107                                 retval = -1;
108                                 break;
109                         }
110                 }
111
112                 if (count < 0) {
113                         condlog(0, "no response from %s", argv[0]);
114                         retval = -1;
115                 }
116
117                 if (i > 0 && value[i-1] == '\n')
118                         i--;
119                 value[i] = '\0';
120
121                 wait(&status);
122                 close(fds[0]);
123
124                 retval = -1;
125                 if (WIFEXITED(status)) {
126                         status = WEXITSTATUS(status);
127                         if (status == 0)
128                                 retval = 0;
129                         else
130                                 condlog(0, "%s exited with %d", argv[0], status);
131                 }
132                 else if (WIFSIGNALED(status))
133                         condlog(0, "%s was terminated by signal %d", argv[0], WTERMSIG(status));
134                 else
135                         condlog(0, "%s terminated abnormally", argv[0]);
136         }
137         return retval;
138 }
139
140 extern int
141 apply_format (char * string, char * cmd, struct path * pp)
142 {
143         char * pos;
144         char * dst;
145         char * p;
146         char * q;
147         int len;
148         int myfree;
149
150         if (!string)
151                 return 1;
152
153         if (!cmd)
154                 return 1;
155
156         dst = cmd;
157         p = dst;
158         pos = strchr(string, '%');
159         myfree = CALLOUT_MAX_SIZE;
160
161         if (!pos) {
162                 strcpy(dst, string);
163                 return 0;
164         }
165
166         len = (int) (pos - string) + 1;
167         myfree -= len;
168
169         if (myfree < 2)
170                 return 1;
171
172         snprintf(p, len, "%s", string);
173         p += len - 1;
174         pos++;
175
176         switch (*pos) {
177         case 'n':
178                 len = strlen(pp->dev) + 1;
179                 myfree -= len;
180
181                 if (myfree < 2)
182                         return 1;
183
184                 snprintf(p, len, "%s", pp->dev);
185                 for (q = p; q < p + len; q++) {
186                         if (q && *q == '!')
187                                 *q = '/';
188                 }
189                 p += len - 1;
190                 break;
191         case 'd':
192                 len = strlen(pp->dev_t) + 1;
193                 myfree -= len;
194
195                 if (myfree < 2)
196                         return 1;
197
198                 snprintf(p, len, "%s", pp->dev_t);
199                 p += len - 1;
200                 break;
201         default:
202                 break;
203         }
204         pos++;
205
206         if (!*pos) {
207                 condlog(3, "formatted callout = %s", dst);
208                 return 0;
209         }
210
211         len = strlen(pos) + 1;
212         myfree -= len;
213
214         if (myfree < 2)
215                 return 1;
216
217         snprintf(p, len, "%s", pos);
218         condlog(3, "reformatted callout = %s", dst);
219         return 0;
220 }