Import 898b5edfe757cb145960b8f3631029bfd5592119 from ctdb:
[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 <errno.h>
11 #include <ccan/tdb/tdb.h>
12 #include <ccan/tap/tap.h>
13 #include <stdio.h>
14
15 static volatile sig_atomic_t alarmed;
16 static void do_alarm(int signum)
17 {
18         alarmed++;
19 }
20
21 static int do_operation(enum operation op, const char *name)
22 {
23         TDB_DATA k = { .dptr = (void *)"a", .dsize = 1 };
24         TDB_DATA d = { .dptr = (void *)"b", .dsize = 1 };
25         struct tdb_context *tdb;
26
27         tdb = tdb_open(name, 0, op == OPEN_WITH_CLEAR_IF_FIRST ?
28                        TDB_CLEAR_IF_FIRST : TDB_DEFAULT, O_RDWR, 0);
29         if (!tdb)
30                 return -1;
31
32         if (op == OPEN || op == OPEN_WITH_CLEAR_IF_FIRST) {
33                 tdb_close(tdb);
34                 return 0;
35         }
36
37         alarmed = 0;
38         tdb_setalarm_sigptr(tdb, &alarmed);
39
40         alarm(1);
41         if (tdb_transaction_start(tdb) != 0)
42                 goto maybe_alarmed;
43
44         if (tdb_store(tdb, k, d, 0) != 0) {
45                 tdb_transaction_cancel(tdb);
46                 tdb_close(tdb);
47                 return -2;
48         }
49
50         if (tdb_transaction_commit(tdb) == 0) {
51                 tdb_delete(tdb, k);
52                 tdb_close(tdb);
53                 return 1;
54         }
55
56         tdb_delete(tdb, k);
57 maybe_alarmed:
58         tdb_close(tdb);
59         if (alarmed)
60                 return 0;
61         return -3;
62 }
63
64 struct agent {
65         int cmdfd, responsefd;
66 };
67
68 /* Do this before doing any tdb stuff.  Return handle, or NULL. */
69 struct agent *prepare_external_agent(void)
70 {
71         int pid;
72         int command[2], response[2];
73         struct sigaction act = { .sa_handler = do_alarm };
74         char name[1+PATH_MAX];
75
76         if (pipe(command) != 0 || pipe(response) != 0)
77                 return NULL;
78
79         pid = fork();
80         if (pid < 0)
81                 return NULL;
82
83         if (pid != 0) {
84                 struct agent *agent = malloc(sizeof(*agent));
85
86                 close(command[0]);
87                 close(response[1]);
88                 agent->cmdfd = command[1];
89                 agent->responsefd = response[0];
90                 return agent;
91         }
92
93         close(command[1]);
94         close(response[0]);
95         sigaction(SIGALRM, &act, NULL);
96
97         while (read(command[0], name, sizeof(name)) != 0) {
98                 int result;
99
100                 result = do_operation(name[0], name+1);
101                 if (write(response[1], &result, sizeof(result))
102                     != sizeof(result))
103                         err(1, "Writing response");
104         }
105         exit(0);
106 }
107
108 /* Ask the external agent to try to do an operation. */
109 bool external_agent_operation(struct agent *agent,
110                               enum operation op, const char *tdbname)
111 {
112         int res;
113         char string[1 + strlen(tdbname) + 1];
114
115         string[0] = op;
116         strcpy(string+1, tdbname);
117
118         if (write(agent->cmdfd, string, sizeof(string)) != sizeof(string))
119                 err(1, "Writing to agent");
120
121         if (read(agent->responsefd, &res, sizeof(res)) != sizeof(res))
122                 err(1, "Reading from agent");
123
124         if (res > 1)
125                 errx(1, "Agent returned %u\n", res);
126
127         return res;
128 }
129
130 void external_agent_operation_start(struct agent *agent,
131                                     enum operation op, const char *tdbname)
132 {
133         char string[1 + strlen(tdbname) + 1];
134
135         string[0] = op;
136         strcpy(string+1, tdbname);
137
138         if (write(agent->cmdfd, string, sizeof(string)) != sizeof(string))
139                 err(1, "Writing to agent");
140 }
141
142 bool external_agent_operation_check(struct agent *agent, bool block, int *res)
143 {
144         int flags = fcntl(agent->responsefd, F_GETFL);
145
146         if (block)
147                 fcntl(agent->responsefd, F_SETFL, flags & ~O_NONBLOCK);
148         else
149                 fcntl(agent->responsefd, F_SETFL, flags | O_NONBLOCK);
150
151         switch (read(agent->responsefd, res, sizeof(*res))) {
152         case sizeof(*res):
153                 break;
154         case 0:
155                 errx(1, "Agent died?");
156         default:
157                 if (!block && (errno == EAGAIN || errno == EWOULDBLOCK))
158                         return false;
159                 err(1, "%slocking reading from agent", block ? "B" : "Non-b");
160         }
161
162         if (*res > 1)
163                 errx(1, "Agent returned %u\n", *res);
164
165         return true;
166 }