1 #include "external-agent.h"
3 #include "lock-tracking.h"
13 #include <ccan/tdb2/private.h>
14 #include <ccan/tap/tap.h>
18 static struct tdb_context
*tdb
;
20 static enum TDB_ERROR
clear_if_first(int fd
, void *arg
)
22 /* We hold a lock offset 63 always, so we can tell if anyone is holding it. */
26 fl
.l_whence
= SEEK_SET
;
30 if (fcntl(fd
, F_SETLK
, &fl
) == 0) {
31 /* We must be first ones to open it! */
32 diag("agent truncating file!");
33 if (ftruncate(fd
, 0) != 0) {
38 if (fcntl(fd
, F_SETLKW
, &fl
) != 0) {
44 static enum agent_return
do_operation(enum operation op
, const char *name
)
47 enum agent_return ret
;
50 union tdb_attribute cif
;
52 if (op
!= OPEN
&& op
!= OPEN_WITH_HOOK
&& !tdb
) {
53 diag("external: No tdb open!");
57 diag("external: %s", operation_name(op
));
59 k
= tdb_mkdata(name
, strlen(name
));
61 locking_would_block
= 0;
65 diag("Already have tdb %s open", tdb
->name
);
68 tdb
= tdb_open(name
, TDB_DEFAULT
, O_RDWR
, 0, &tap_log_attr
);
70 if (!locking_would_block
)
71 diag("Opening tdb gave %s", strerror(errno
));
79 diag("Already have tdb %s open", tdb
->name
);
82 cif
.openhook
.base
.attr
= TDB_ATTRIBUTE_OPENHOOK
;
83 cif
.openhook
.base
.next
= &tap_log_attr
;
84 cif
.openhook
.fn
= clear_if_first
;
85 tdb
= tdb_open(name
, TDB_DEFAULT
, O_RDWR
, 0, &cif
);
87 if (!locking_would_block
)
88 diag("Opening tdb gave %s", strerror(errno
));
95 ecode
= tdb_fetch(tdb
, k
, &data
);
96 if (ecode
== TDB_ERR_NOEXIST
) {
98 } else if (ecode
< 0) {
100 } else if (!tdb_deq(data
, k
)) {
109 ret
= tdb_store(tdb
, k
, k
, 0) == 0 ? SUCCESS
: OTHER_FAILURE
;
111 case TRANSACTION_START
:
112 ret
= tdb_transaction_start(tdb
) == 0 ? SUCCESS
: OTHER_FAILURE
;
114 case TRANSACTION_COMMIT
:
115 ret
= tdb_transaction_commit(tdb
)==0 ? SUCCESS
: OTHER_FAILURE
;
118 ret
= tdb_needs_recovery(tdb
) ? SUCCESS
: FAILED
;
121 ret
= tdb_check(tdb
, NULL
, NULL
) == 0 ? SUCCESS
: OTHER_FAILURE
;
124 ret
= tdb_close(tdb
) == 0 ? SUCCESS
: OTHER_FAILURE
;
128 /* We do this async */
135 if (locking_would_block
)
136 ret
= WOULD_HAVE_BLOCKED
;
142 int cmdfd
, responsefd
;
145 /* Do this before doing any tdb stuff. Return handle, or NULL. */
146 struct agent
*prepare_external_agent(void)
149 int command
[2], response
[2];
150 char name
[1+PATH_MAX
];
152 if (pipe(command
) != 0 || pipe(response
) != 0)
160 struct agent
*agent
= malloc(sizeof(*agent
));
164 agent
->cmdfd
= command
[1];
165 agent
->responsefd
= response
[0];
172 /* We want to fail, not block. */
173 nonblocking_locks
= true;
174 log_prefix
= "external: ";
175 while ((ret
= read(command
[0], name
, sizeof(name
))) > 0) {
176 enum agent_return result
;
178 result
= do_operation(name
[0], name
+1);
179 if (write(response
[1], &result
, sizeof(result
))
181 err(1, "Writing response");
182 if (name
[0] == SEND_SIGNAL
) {
183 struct timeval ten_ms
;
185 ten_ms
.tv_usec
= 10000;
186 select(0, NULL
, NULL
, NULL
, &ten_ms
);
187 kill(getppid(), SIGUSR1
);
193 /* Ask the external agent to try to do an operation. */
194 enum agent_return
external_agent_operation(struct agent
*agent
,
198 enum agent_return res
;
204 len
= 1 + strlen(name
) + 1;
205 string
= malloc(len
);
208 strcpy(string
+1, name
);
210 if (write(agent
->cmdfd
, string
, len
) != len
211 || read(agent
->responsefd
, &res
, sizeof(res
)) != sizeof(res
))
218 const char *agent_return_name(enum agent_return ret
)
220 return ret
== SUCCESS
? "SUCCESS"
221 : ret
== WOULD_HAVE_BLOCKED
? "WOULD_HAVE_BLOCKED"
222 : ret
== AGENT_DIED
? "AGENT_DIED"
223 : ret
== FAILED
? "FAILED"
224 : ret
== OTHER_FAILURE
? "OTHER_FAILURE"
228 const char *operation_name(enum operation op
)
231 case OPEN
: return "OPEN";
232 case OPEN_WITH_HOOK
: return "OPEN_WITH_HOOK";
233 case FETCH
: return "FETCH";
234 case STORE
: return "STORE";
235 case CHECK
: return "CHECK";
236 case TRANSACTION_START
: return "TRANSACTION_START";
237 case TRANSACTION_COMMIT
: return "TRANSACTION_COMMIT";
238 case NEEDS_RECOVERY
: return "NEEDS_RECOVERY";
239 case SEND_SIGNAL
: return "SEND_SIGNAL";
240 case CLOSE
: return "CLOSE";
242 return "**INVALID**";
245 void free_external_agent(struct agent
*agent
)
248 close(agent
->responsefd
);