Line data Source code
1 : #include "external-agent.h"
2 : #include "lock-tracking.h"
3 : #include "logging.h"
4 : #include <sys/types.h>
5 : #include <sys/wait.h>
6 : #include <unistd.h>
7 : #include <fcntl.h>
8 : #include <stdlib.h>
9 : #include <limits.h>
10 : #include <string.h>
11 : #include <errno.h>
12 : #include "../common/tdb_private.h"
13 : #include "tap-interface.h"
14 : #include <stdio.h>
15 : #include <stdarg.h>
16 :
17 : static struct tdb_context *tdb;
18 :
19 410 : static enum agent_return do_operation(enum operation op, const char *name)
20 : {
21 : TDB_DATA k;
22 : enum agent_return ret;
23 : TDB_DATA data;
24 :
25 410 : if (op != OPEN && op != OPEN_WITH_CLEAR_IF_FIRST && !tdb) {
26 0 : diag("external: No tdb open!");
27 0 : return OTHER_FAILURE;
28 : }
29 :
30 410 : k.dptr = discard_const_p(uint8_t, name);
31 410 : k.dsize = strlen(name);
32 :
33 410 : locking_would_block = 0;
34 410 : switch (op) {
35 68 : case OPEN:
36 68 : if (tdb) {
37 0 : diag("Already have tdb %s open", tdb_name(tdb));
38 0 : return OTHER_FAILURE;
39 : }
40 68 : tdb = tdb_open_ex(name, 0, TDB_DEFAULT, O_RDWR, 0,
41 : &taplogctx, NULL);
42 68 : if (!tdb) {
43 10 : if (!locking_would_block)
44 0 : diag("Opening tdb gave %s", strerror(errno));
45 10 : ret = OTHER_FAILURE;
46 : } else
47 58 : ret = SUCCESS;
48 68 : break;
49 12 : case OPEN_WITH_CLEAR_IF_FIRST:
50 12 : if (tdb)
51 0 : return OTHER_FAILURE;
52 12 : tdb = tdb_open_ex(name, 0, TDB_CLEAR_IF_FIRST, O_RDWR, 0,
53 : &taplogctx, NULL);
54 12 : ret = tdb ? SUCCESS : OTHER_FAILURE;
55 12 : break;
56 26 : case TRANSACTION_START:
57 26 : ret = tdb_transaction_start(tdb) == 0 ? SUCCESS : OTHER_FAILURE;
58 26 : break;
59 71 : case FETCH:
60 71 : data = tdb_fetch(tdb, k);
61 71 : if (data.dptr == NULL) {
62 0 : if (tdb_error(tdb) == TDB_ERR_NOEXIST)
63 0 : ret = FAILED;
64 : else
65 0 : ret = OTHER_FAILURE;
66 71 : } else if (data.dsize != k.dsize
67 71 : || memcmp(data.dptr, k.dptr, k.dsize) != 0) {
68 0 : ret = OTHER_FAILURE;
69 : } else {
70 71 : ret = SUCCESS;
71 : }
72 71 : free(data.dptr);
73 71 : break;
74 19 : case STORE:
75 19 : ret = tdb_store(tdb, k, k, 0) == 0 ? SUCCESS : OTHER_FAILURE;
76 19 : break;
77 3 : case TRANSACTION_COMMIT:
78 3 : ret = tdb_transaction_commit(tdb)==0 ? SUCCESS : OTHER_FAILURE;
79 3 : break;
80 51 : case CHECK:
81 51 : ret = tdb_check(tdb, NULL, NULL) == 0 ? SUCCESS : OTHER_FAILURE;
82 51 : break;
83 102 : case NEEDS_RECOVERY:
84 102 : ret = tdb_needs_recovery(tdb) ? SUCCESS : FAILED;
85 102 : break;
86 58 : case CLOSE:
87 58 : ret = tdb_close(tdb) == 0 ? SUCCESS : OTHER_FAILURE;
88 58 : tdb = NULL;
89 58 : break;
90 0 : case PING:
91 0 : ret = SUCCESS;
92 0 : break;
93 0 : case UNMAP:
94 0 : ret = tdb_munmap(tdb) == 0 ? SUCCESS : OTHER_FAILURE;
95 0 : if (ret == SUCCESS) {
96 0 : tdb->flags |= TDB_NOMMAP;
97 : }
98 0 : break;
99 0 : default:
100 0 : ret = OTHER_FAILURE;
101 : }
102 :
103 410 : if (locking_would_block)
104 27 : ret = WOULD_HAVE_BLOCKED;
105 :
106 410 : return ret;
107 : }
108 :
109 : struct agent {
110 : int cmdfd, responsefd;
111 : pid_t pid;
112 : };
113 :
114 : /* Do this before doing any tdb stuff. Return handle, or NULL. */
115 37 : struct agent *prepare_external_agent(void)
116 : {
117 : int ret;
118 : int command[2], response[2];
119 : char name[1+PATH_MAX];
120 37 : struct agent *agent = malloc(sizeof(*agent));
121 :
122 37 : if (pipe(command) != 0 || pipe(response) != 0) {
123 0 : fprintf(stderr, "pipe failed: %s\n", strerror(errno));
124 0 : exit(1);
125 : }
126 :
127 37 : agent->pid = fork();
128 41 : if (agent->pid < 0) {
129 0 : fprintf(stderr, "fork failed: %s\n", strerror(errno));
130 0 : exit(1);
131 : }
132 :
133 41 : if (agent->pid != 0) {
134 37 : close(command[0]);
135 37 : close(response[1]);
136 37 : agent->cmdfd = command[1];
137 37 : agent->responsefd = response[0];
138 37 : return agent;
139 : }
140 :
141 4 : close(command[1]);
142 4 : close(response[0]);
143 :
144 : /* We want to fail, not block. */
145 4 : nonblocking_locks = true;
146 4 : log_prefix = "external: ";
147 414 : while ((ret = read(command[0], name, sizeof(name))) > 0) {
148 : enum agent_return result;
149 :
150 410 : result = do_operation(name[0], name+1);
151 410 : if (write(response[1], &result, sizeof(result))
152 : != sizeof(result))
153 0 : abort();
154 : }
155 4 : exit(0);
156 : }
157 :
158 33 : void shutdown_agent(struct agent *agent)
159 : {
160 : pid_t p;
161 :
162 33 : close(agent->cmdfd);
163 33 : close(agent->responsefd);
164 33 : p = waitpid(agent->pid, NULL, WNOHANG);
165 33 : if (p == 0) {
166 1 : kill(agent->pid, SIGKILL);
167 : }
168 33 : waitpid(agent->pid, NULL, 0);
169 33 : free(agent);
170 33 : }
171 :
172 : /* Ask the external agent to try to do an operation. */
173 532 : enum agent_return external_agent_operation(struct agent *agent,
174 : enum operation op,
175 : const char *name)
176 : {
177 : enum agent_return res;
178 : unsigned int len;
179 : char *string;
180 :
181 532 : if (!name)
182 4 : name = "";
183 532 : len = 1 + strlen(name) + 1;
184 532 : string = malloc(len);
185 :
186 532 : string[0] = op;
187 532 : strncpy(string+1, name, len - 1);
188 532 : string[len-1] = '\0';
189 :
190 532 : if (write(agent->cmdfd, string, len) != len
191 532 : || read(agent->responsefd, &res, sizeof(res)) != sizeof(res))
192 32 : res = AGENT_DIED;
193 :
194 532 : free(string);
195 532 : return res;
196 : }
197 :
198 65 : const char *agent_return_name(enum agent_return ret)
199 : {
200 : return ret == SUCCESS ? "SUCCESS"
201 129 : : ret == WOULD_HAVE_BLOCKED ? "WOULD_HAVE_BLOCKED"
202 128 : : ret == AGENT_DIED ? "AGENT_DIED"
203 64 : : ret == FAILED ? "FAILED"
204 0 : : ret == OTHER_FAILURE ? "OTHER_FAILURE"
205 0 : : "**INVALID**";
206 : }
207 :
208 3 : const char *operation_name(enum operation op)
209 : {
210 3 : switch (op) {
211 0 : case OPEN: return "OPEN";
212 0 : case OPEN_WITH_CLEAR_IF_FIRST: return "OPEN_WITH_CLEAR_IF_FIRST";
213 1 : case TRANSACTION_START: return "TRANSACTION_START";
214 1 : case FETCH: return "FETCH";
215 1 : case STORE: return "STORE";
216 0 : case TRANSACTION_COMMIT: return "TRANSACTION_COMMIT";
217 0 : case CHECK: return "CHECK";
218 0 : case NEEDS_RECOVERY: return "NEEDS_RECOVERY";
219 0 : case CLOSE: return "CLOSE";
220 0 : case PING: return "PING";
221 0 : case UNMAP: return "UNMAP";
222 : }
223 0 : return "**INVALID**";
224 : }
|