Don't rely on terrible dup2 trick, use proper infrastructure for external agent.
[ccan] / ccan / tdb / test / external-transaction.c
1 #include "external-transaction.h"
2 #include <sys/types.h>
3 #include <sys/wait.h>
4 #include <unistd.h>
5 #include <err.h>
6 #include <fcntl.h>
7 #include <stdlib.h>
8 #include <limits.h>
9 #include <string.h>
10 #include <ccan/tdb/tdb.h>
11
12 static volatile sig_atomic_t alarmed;
13 static void do_alarm(int signum)
14 {
15         alarmed++;
16 }
17
18 static int do_transaction(const char *name)
19 {
20         TDB_DATA k = { .dptr = (void *)"a", .dsize = 1 };
21         TDB_DATA d = { .dptr = (void *)"b", .dsize = 1 };
22         struct tdb_context *tdb = tdb_open(name, 0, 0, O_RDWR, 0);
23
24         if (!tdb)
25                 return -1;
26
27         alarmed = 0;
28         tdb_setalarm_sigptr(tdb, &alarmed);
29
30         alarm(1);
31         if (tdb_transaction_start(tdb) != 0)
32                 goto maybe_alarmed;
33
34         if (tdb_store(tdb, k, d, 0) != 0) {
35                 tdb_transaction_cancel(tdb);
36                 tdb_close(tdb);
37                 return -2;
38         }
39
40         if (tdb_transaction_commit(tdb) == 0) {
41                 tdb_delete(tdb, k);
42                 tdb_close(tdb);
43                 return 1;
44         }
45
46         tdb_delete(tdb, k);
47 maybe_alarmed:
48         tdb_close(tdb);
49         if (alarmed)
50                 return 0;
51         return -3;
52 }
53
54 struct agent {
55         int cmdfd, responsefd;
56 };
57
58 /* Do this before doing any tdb stuff.  Return handle, or NULL. */
59 struct agent *prepare_external_agent(void)
60 {
61         int pid;
62         int command[2], response[2];
63         struct sigaction act = { .sa_handler = do_alarm };
64         char name[PATH_MAX];
65
66         if (pipe(command) != 0 || pipe(response) != 0)
67                 return NULL;
68
69         pid = fork();
70         if (pid < 0)
71                 return NULL;
72
73         if (pid != 0) {
74                 struct agent *agent = malloc(sizeof(*agent));
75
76                 close(command[0]);
77                 close(response[1]);
78                 agent->cmdfd = command[1];
79                 agent->responsefd = response[0];
80                 return agent;
81         }
82
83         close(command[1]);
84         close(response[0]);
85         sigaction(SIGALRM, &act, NULL);
86
87         while (read(command[0], name, sizeof(name)) != 0) {
88                 int result = do_transaction(name);
89                 if (write(response[1], &result, sizeof(result))
90                     != sizeof(result))
91                         err(1, "Writing response");
92         }
93         exit(0);
94 }
95
96 /* Ask the external agent to try to do a transaction. */
97 bool external_agent_transaction(struct agent *agent, const char *tdbname)
98 {
99         int res;
100
101         if (write(agent->cmdfd, tdbname, strlen(tdbname)+1)
102             != strlen(tdbname)+1)
103                 err(1, "Writing to agent");
104
105         if (read(agent->responsefd, &res, sizeof(res)) != sizeof(res))
106                 err(1, "Reading from agent");
107
108         if (res > 1)
109                 errx(1, "Agent returned %u\n", res);
110
111         return res;
112 }