1 #include "external-agent.h"
3 #include "lock-tracking.h"
7 #include <ccan/err/err.h>
13 #include "tap-interface.h"
17 static struct ntdb_context
*ntdb
;
19 void (*external_agent_free
)(void *) = free
;
21 static enum NTDB_ERROR
clear_if_first(int fd
, void *arg
)
23 /* We hold a lock offset 4 always, so we can tell if anyone is holding it.
24 * (This is compatible with tdb's TDB_CLEAR_IF_FIRST flag). */
28 fl
.l_whence
= SEEK_SET
;
32 if (fcntl(fd
, F_SETLK
, &fl
) == 0) {
33 /* We must be first ones to open it! */
34 diag("agent truncating file!");
35 if (ftruncate(fd
, 0) != 0) {
40 if (fcntl(fd
, F_SETLKW
, &fl
) != 0) {
46 static enum agent_return
do_operation(enum operation op
, const char *name
)
49 enum agent_return ret
;
51 enum NTDB_ERROR ecode
;
52 union ntdb_attribute cif
;
55 if (op
!= OPEN
&& op
!= OPEN_WITH_HOOK
&& !ntdb
) {
56 diag("external: No ntdb open!");
60 diag("external: %s", operation_name(op
));
62 eq
= strchr(name
, '=');
64 k
= ntdb_mkdata(name
, eq
- name
);
65 d
= ntdb_mkdata(eq
+ 1, strlen(eq
+1));
67 k
= ntdb_mkdata(name
, strlen(name
));
72 locking_would_block
= 0;
76 diag("Already have ntdb %s open", ntdb_name(ntdb
));
79 ntdb
= ntdb_open(name
, MAYBE_NOSYNC
, O_RDWR
, 0, &tap_log_attr
);
81 if (!locking_would_block
)
82 diag("Opening ntdb gave %s", strerror(errno
));
90 diag("Already have ntdb %s open", ntdb_name(ntdb
));
93 cif
.openhook
.base
.attr
= NTDB_ATTRIBUTE_OPENHOOK
;
94 cif
.openhook
.base
.next
= &tap_log_attr
;
95 cif
.openhook
.fn
= clear_if_first
;
96 ntdb
= ntdb_open(name
, MAYBE_NOSYNC
, O_RDWR
, 0, &cif
);
98 if (!locking_would_block
)
99 diag("Opening ntdb gave %s", strerror(errno
));
106 ecode
= ntdb_fetch(ntdb
, k
, &data
);
107 if (ecode
== NTDB_ERR_NOEXIST
) {
109 } else if (ecode
< 0) {
111 } else if (!ntdb_deq(data
, d
)) {
113 external_agent_free(data
.dptr
);
116 external_agent_free(data
.dptr
);
120 ret
= ntdb_store(ntdb
, k
, d
, 0) == 0 ? SUCCESS
: OTHER_FAILURE
;
122 case TRANSACTION_START
:
123 ret
= ntdb_transaction_start(ntdb
) == 0 ? SUCCESS
: OTHER_FAILURE
;
125 case TRANSACTION_COMMIT
:
126 ret
= ntdb_transaction_commit(ntdb
)==0 ? SUCCESS
: OTHER_FAILURE
;
129 ret
= external_agent_needs_rec(ntdb
);
132 ret
= ntdb_check(ntdb
, NULL
, NULL
) == 0 ? SUCCESS
: OTHER_FAILURE
;
135 ret
= ntdb_close(ntdb
) == 0 ? SUCCESS
: OTHER_FAILURE
;
139 /* We do this async */
146 if (locking_would_block
)
147 ret
= WOULD_HAVE_BLOCKED
;
153 int cmdfd
, responsefd
;
156 /* Do this before doing any ntdb stuff. Return handle, or NULL. */
157 struct agent
*prepare_external_agent(void)
160 int command
[2], response
[2];
161 char name
[1+PATH_MAX
];
163 if (pipe(command
) != 0 || pipe(response
) != 0)
171 struct agent
*agent
= malloc(sizeof(*agent
));
175 agent
->cmdfd
= command
[1];
176 agent
->responsefd
= response
[0];
183 /* We want to fail, not block. */
184 nonblocking_locks
= true;
185 log_prefix
= "external: ";
186 while ((ret
= read(command
[0], name
, sizeof(name
))) > 0) {
187 enum agent_return result
;
189 result
= do_operation(name
[0], name
+1);
190 if (write(response
[1], &result
, sizeof(result
))
192 err(1, "Writing response");
193 if (name
[0] == SEND_SIGNAL
) {
194 struct timeval ten_ms
;
196 ten_ms
.tv_usec
= 10000;
197 select(0, NULL
, NULL
, NULL
, &ten_ms
);
198 kill(getppid(), SIGUSR1
);
204 /* Ask the external agent to try to do an operation. */
205 enum agent_return
external_agent_operation(struct agent
*agent
,
209 enum agent_return res
;
215 len
= 1 + strlen(name
) + 1;
216 string
= malloc(len
);
219 strcpy(string
+1, name
);
221 if (write(agent
->cmdfd
, string
, len
) != len
222 || read(agent
->responsefd
, &res
, sizeof(res
)) != sizeof(res
))
229 const char *agent_return_name(enum agent_return ret
)
231 return ret
== SUCCESS
? "SUCCESS"
232 : ret
== WOULD_HAVE_BLOCKED
? "WOULD_HAVE_BLOCKED"
233 : ret
== AGENT_DIED
? "AGENT_DIED"
234 : ret
== FAILED
? "FAILED"
235 : ret
== OTHER_FAILURE
? "OTHER_FAILURE"
239 const char *operation_name(enum operation op
)
242 case OPEN
: return "OPEN";
243 case OPEN_WITH_HOOK
: return "OPEN_WITH_HOOK";
244 case FETCH
: return "FETCH";
245 case STORE
: return "STORE";
246 case CHECK
: return "CHECK";
247 case TRANSACTION_START
: return "TRANSACTION_START";
248 case TRANSACTION_COMMIT
: return "TRANSACTION_COMMIT";
249 case NEEDS_RECOVERY
: return "NEEDS_RECOVERY";
250 case SEND_SIGNAL
: return "SEND_SIGNAL";
251 case CLOSE
: return "CLOSE";
253 return "**INVALID**";
256 void free_external_agent(struct agent
*agent
)
259 close(agent
->responsefd
);