1) close pipes after dup'ing
[platform/upstream/rpm.git] / build / reqprov.c
1 /* reqprov.c -- require/provide handling */
2
3 #include <stdlib.h>
4 #include <string.h>
5 #include <unistd.h>
6 #include <signal.h>
7 #include <fcntl.h>
8 #include <ctype.h>
9 #include <sys/types.h>
10 #include <sys/wait.h>
11 #include <sys/stat.h>
12 #include <errno.h>
13
14 #include "specP.h"
15 #include "reqprov.h"
16 #include "messages.h"
17 #include "rpmlib.h"
18 #include "misc.h"
19
20 static StringBuf getOutputFrom(char *dir, char *argv[],
21                                char *writePtr, int writeBytesLeft,
22                                int failNonZero);
23
24 /*************************************************************/
25 /*                                                           */
26 /* Adding entries to the package reqprov list                */
27 /* Handle duplicate entries                                  */
28 /*                                                           */
29 /*************************************************************/
30
31 int addReqProv(struct PackageRec *p, int flags,
32                char *name, char *version)
33 {
34     struct ReqProv *rd;
35     int same;
36
37     /* Frist see if the same entry is already there */
38     rd = p->reqprov;
39     while (rd) {
40         if (rd->flags == flags) {
41             if (rd->version == version) {
42                 same = 1;
43             } else if (!rd->version || !version) {
44                 same = 0;
45             } else if (!strcmp(rd->version, version)) {
46                 same = 1;
47             } else {
48                 same = 0;
49             }
50
51             if (same && !strcmp(rd->name, name)) {
52                 /* They are exacty the same */
53                 break;
54             }
55         }
56         rd = rd->next;
57     }
58     if (rd) {
59         /* already there */
60         rpmMessage(RPMMESS_DEBUG, "Already Got: %s\n", name);
61         return 0;
62     }
63     
64     rd = (struct ReqProv *)malloc(sizeof(*rd));
65     rd->flags = flags;
66     rd->name = strdup(name);
67     rd->version = version ? strdup(version) : NULL;
68     rd->next = p->reqprov;
69     p->reqprov = rd;
70
71     if (flags & RPMSENSE_PROVIDES) {
72         rpmMessage(RPMMESS_DEBUG, "Adding provide: %s\n", name);
73         p->numProv++;
74     } else if (flags & RPMSENSE_CONFLICTS) {
75         rpmMessage(RPMMESS_DEBUG, "Adding conflict: %s\n", name);
76         p->numConflict++;
77     } else {
78         rpmMessage(RPMMESS_DEBUG, "Adding require: %s\n", name);
79         p->numReq++;
80     }
81
82     return 0;
83 }
84
85 /*************************************************************/
86 /*                                                           */
87 /* Add require/provides for the files in the header          */
88 /*  (adds to the package structure)                          */
89 /*                                                           */
90 /*************************************************************/
91
92 static StringBuf getOutputFrom(char *dir, char *argv[],
93                                char *writePtr, int writeBytesLeft,
94                                int failNonZero)
95 {
96     int progPID;
97     int progDead;
98     int toProg[2];
99     int fromProg[2];
100     int status;
101     void *oldhandler;
102     int bytesWritten;
103     StringBuf readBuff;
104     int bytes;
105     unsigned char buf[8193];
106
107     oldhandler = signal(SIGPIPE, SIG_IGN);
108
109     pipe(toProg);
110     pipe(fromProg);
111     
112     if (!(progPID = fork())) {
113         close(0);
114         close(1);
115         close(toProg[1]);
116         close(fromProg[0]);
117         
118         dup2(toProg[0], 0);   /* Make stdin the in pipe */
119         dup2(fromProg[1], 1); /* Make stdout the out pipe */
120
121         close(toProg[0]);
122         close(fromProg[1]);
123
124         chdir(dir);
125         
126         execvp(argv[0], argv);
127         rpmError(RPMERR_EXEC, "Couldn't exec %s", argv[0]);
128         _exit(RPMERR_EXEC);
129     }
130     if (progPID < 0) {
131         rpmError(RPMERR_FORK, "Couldn't fork %s", argv[0]);
132         return NULL;
133     }
134
135     close(toProg[0]);
136     close(fromProg[1]);
137
138     /* Do not block reading or writing from/to prog. */
139     fcntl(fromProg[0], F_SETFL, O_NONBLOCK);
140     fcntl(toProg[1], F_SETFL, O_NONBLOCK);
141     
142     readBuff = newStringBuf();
143
144     progDead = 0;
145     do {
146         if (waitpid(progPID, &status, WNOHANG)) {
147             progDead = 1;
148         }
149
150         /* Write some stuff to the process if possible */
151         if (writeBytesLeft) {
152             if ((bytesWritten =
153                   write(toProg[1], writePtr,
154                     (1024<writeBytesLeft) ? 1024 : writeBytesLeft)) < 0) {
155                 if (errno != EAGAIN) {
156                     perror("getOutputFrom()");
157                     exit(1);
158                 }
159                 bytesWritten = 0;
160             }
161             writeBytesLeft -= bytesWritten;
162             writePtr += bytesWritten;
163         } else {
164             close(toProg[1]);
165         }
166         
167         /* Read any data from prog */
168         bytes = read(fromProg[0], buf, sizeof(buf)-1);
169         while (bytes > 0) {
170             buf[bytes] = '\0';
171             appendStringBuf(readBuff, buf);
172             bytes = read(fromProg[0], buf, sizeof(buf)-1);
173         }
174
175         /* terminate when prog dies */
176     } while (!progDead);
177
178     close(toProg[1]);
179     close(fromProg[0]);
180     signal(SIGPIPE, oldhandler);
181
182     if (writeBytesLeft) {
183         rpmError(RPMERR_EXEC, "failed to write all data to %s", argv[0]);
184         return NULL;
185     }
186     waitpid(progPID, &status, 0);
187     if (failNonZero && (!WIFEXITED(status) || WEXITSTATUS(status))) {
188         rpmError(RPMERR_EXEC, "%s failed", argv[0]);
189         return NULL;
190     }
191
192     return readBuff;
193 }
194
195 int generateAutoReqProv(Header header, struct PackageRec *p)
196 {
197     char **f, **fsave, *s;
198     int count;
199     int_16 *modes;
200
201     StringBuf writeBuff;
202     StringBuf readBuff;
203     char *writePtr;
204     int writeBytes;
205     char dir[1024];
206     char *argv[8];
207
208     rpmMessage(RPMMESS_VERBOSE, "Finding dependencies...\n");
209
210     /*** Get root directory ***/
211     
212     if (rpmGetVar(RPMVAR_ROOT)) {
213         strcpy(dir, rpmGetVar(RPMVAR_ROOT));
214     } else {
215         strcpy(dir, "/");
216     }
217
218     /*** Generate File List ***/
219     
220     if (!headerGetEntry(header, RPMTAG_FILENAMES, NULL, (void **) &f, &count)) {
221         return 0;
222     }
223     if (!count) {
224         return 0;
225     }
226     fsave = f;
227     headerGetEntry(header, RPMTAG_FILEMODES, NULL, (void **) &modes, NULL);
228
229     writeBuff = newStringBuf();
230     writeBytes = 0;
231     while (count--) {
232         s = *f++;
233         /* We skip the leading "/" (already normalized) */
234         writeBytes += strlen(s);
235         appendLineStringBuf(writeBuff, s + 1);
236     }
237     if (fsave) {
238         free(fsave);
239     }
240     writePtr = getStringBuf(writeBuff);
241
242     /*** Do Provides ***/
243     
244     argv[0] = "find-provides";
245     argv[1] = NULL;
246     readBuff = getOutputFrom(dir, argv, writePtr, writeBytes, 1);
247     if (!readBuff) {
248         rpmError(RPMERR_EXEC, "Failed to find provides");
249         exit(1);
250     }
251     
252     s = getStringBuf(readBuff);
253     f = fsave = splitString(s, strlen(s), '\n');
254     freeStringBuf(readBuff);
255     while (*f) {
256         if (**f) {
257             addReqProv(p, RPMSENSE_PROVIDES, *f, NULL);
258         }
259         f++;
260     }
261     free(fsave);
262
263     /*** Do Requires ***/
264     
265     argv[0] = "find-requires";
266     argv[1] = NULL;
267     readBuff = getOutputFrom(dir, argv, writePtr, writeBytes, 0);
268     if (!readBuff) {
269         rpmError(RPMERR_EXEC, "Failed to find requires");
270         exit(1);
271     }
272
273     s = getStringBuf(readBuff);
274     f = fsave = splitString(s, strlen(s), '\n');
275     freeStringBuf(readBuff);
276     while (*f) {
277         if (**f) {
278             addReqProv(p, RPMSENSE_ANY, *f, NULL);
279         }
280         f++;
281     }
282     free(fsave);
283
284     /*** Clean Up ***/
285
286     freeStringBuf(writeBuff);
287
288     return 0;
289 }
290
291 /*************************************************************/
292 /*                                                           */
293 /* Generate and add header entry from package record         */
294 /*                                                           */
295 /*************************************************************/
296
297 int processReqProv(Header h, struct PackageRec *p)
298 {
299     struct ReqProv *rd;
300     char **nameArray, **namePtr;
301     char **versionArray, **versionPtr;
302     int_32 *flagArray, *flagPtr;
303     
304
305     if (p->numProv) {
306         rd = p->reqprov;
307         nameArray = namePtr = malloc(p->numProv * sizeof(*nameArray));
308         rpmMessage(RPMMESS_VERBOSE, "Provides (%d):", p->numProv);
309         while (rd) {
310             if (rd->flags & RPMSENSE_PROVIDES) {
311                 rpmMessage(RPMMESS_VERBOSE, " %s", rd->name);
312                 *namePtr++ = rd->name;
313             }
314             rd = rd->next;
315         }
316         rpmMessage(RPMMESS_VERBOSE, "\n");
317
318         headerAddEntry(h, RPMTAG_PROVIDES, RPM_STRING_ARRAY_TYPE, nameArray, p->numProv);
319         free(nameArray);
320     }
321
322     if (p->numConflict) {
323         rd = p->reqprov;
324         nameArray = namePtr = malloc(p->numConflict * sizeof(*nameArray));
325         versionArray = versionPtr =
326             malloc(p->numConflict * sizeof(*versionArray));
327         flagArray = flagPtr = malloc(p->numConflict * sizeof(*flagArray));
328         rpmMessage(RPMMESS_VERBOSE, "Conflicts (%d):", p->numConflict);
329         while (rd) {
330             if (rd->flags & RPMSENSE_CONFLICTS) {
331                 rpmMessage(RPMMESS_VERBOSE, " %s", rd->name);
332                 *namePtr++ = rd->name;
333                 *versionPtr++ = rd->version ? rd->version : "";
334                 *flagPtr++ = rd->flags & RPMSENSE_SENSEMASK;
335             }
336             rd = rd->next;
337         }
338         rpmMessage(RPMMESS_VERBOSE, "\n");
339
340         headerAddEntry(h, RPMTAG_CONFLICTNAME, RPM_STRING_ARRAY_TYPE,
341                  nameArray, p->numConflict);
342         headerAddEntry(h, RPMTAG_CONFLICTVERSION, RPM_STRING_ARRAY_TYPE,
343                  versionArray, p->numConflict);
344         headerAddEntry(h, RPMTAG_CONFLICTFLAGS, RPM_INT32_TYPE,
345                  flagArray, p->numConflict);
346
347         free(nameArray);
348         free(versionArray);
349         free(flagArray);
350     }
351
352     if (p->numReq) {
353         rd = p->reqprov;
354         nameArray = namePtr = malloc(p->numReq * sizeof(*nameArray));
355         versionArray = versionPtr = malloc(p->numReq * sizeof(*versionArray));
356         flagArray = flagPtr = malloc(p->numReq * sizeof(*flagArray));
357         rpmMessage(RPMMESS_VERBOSE, "Requires (%d):", p->numReq);
358         while (rd) {
359             if (! ((rd->flags & RPMSENSE_PROVIDES) ||
360                    (rd->flags & RPMSENSE_CONFLICTS))) {
361                 rpmMessage(RPMMESS_VERBOSE, " %s", rd->name);
362                 *namePtr++ = rd->name;
363                 *versionPtr++ = rd->version ? rd->version : "";
364                 *flagPtr++ = rd->flags & RPMSENSE_SENSEMASK;
365             }
366             rd = rd->next;
367         }
368         rpmMessage(RPMMESS_VERBOSE, "\n");
369
370         headerAddEntry(h, RPMTAG_REQUIRENAME, RPM_STRING_ARRAY_TYPE,
371                  nameArray, p->numReq);
372         headerAddEntry(h, RPMTAG_REQUIREVERSION, RPM_STRING_ARRAY_TYPE,
373                  versionArray, p->numReq);
374         headerAddEntry(h, RPMTAG_REQUIREFLAGS, RPM_INT32_TYPE,
375                  flagArray, p->numReq);
376
377         free(nameArray);
378         free(versionArray);
379         free(flagArray);
380     }
381
382     return 0;
383 }