1 #include "external-agent.h"
3 #include "lock-tracking.h"
13 #include <ccan/tdb2/tdb1_private.h>
14 #include <ccan/tap/tap.h>
18 static struct tdb_context
*tdb
;
20 void (*external_agent_free
)(void *) = free
;
22 static enum TDB_ERROR
clear_if_first(int fd
, void *arg
)
24 /* We hold a lock offset 4 always, so we can tell if anyone is holding it.
25 * (This is compatible with tdb1's TDB_CLEAR_IF_FIRST flag). */
29 fl
.l_whence
= SEEK_SET
;
33 if (fcntl(fd
, F_SETLK
, &fl
) == 0) {
34 /* We must be first ones to open it! */
35 diag("agent truncating file!");
36 if (ftruncate(fd
, 0) != 0) {
41 if (fcntl(fd
, F_SETLKW
, &fl
) != 0) {
47 static enum agent_return
do_operation(enum operation op
, const char *name
)
50 enum agent_return ret
;
53 union tdb_attribute cif
;
55 if (op
!= OPEN
&& op
!= OPEN_WITH_HOOK
&& !tdb
) {
56 diag("external: No tdb open!");
60 diag("external: %s", operation_name(op
));
62 k
= tdb_mkdata(name
, strlen(name
));
64 locking_would_block
= 0;
68 diag("Already have tdb %s open", tdb
->name
);
71 tdb
= tdb_open(name
, TDB_DEFAULT
, O_RDWR
, 0, &tap_log_attr
);
73 if (!locking_would_block
)
74 diag("Opening tdb gave %s", strerror(errno
));
82 diag("Already have tdb %s open", tdb
->name
);
85 cif
.openhook
.base
.attr
= TDB_ATTRIBUTE_OPENHOOK
;
86 cif
.openhook
.base
.next
= &tap_log_attr
;
87 cif
.openhook
.fn
= clear_if_first
;
88 tdb
= tdb_open(name
, TDB_DEFAULT
, O_RDWR
, 0, &cif
);
90 if (!locking_would_block
)
91 diag("Opening tdb gave %s", strerror(errno
));
98 ecode
= tdb_fetch(tdb
, k
, &data
);
99 if (ecode
== TDB_ERR_NOEXIST
) {
101 } else if (ecode
< 0) {
103 } else if (!tdb_deq(data
, k
)) {
105 external_agent_free(data
.dptr
);
108 external_agent_free(data
.dptr
);
112 ret
= tdb_store(tdb
, k
, k
, 0) == 0 ? SUCCESS
: OTHER_FAILURE
;
114 case TRANSACTION_START
:
115 ret
= tdb_transaction_start(tdb
) == 0 ? SUCCESS
: OTHER_FAILURE
;
117 case TRANSACTION_COMMIT
:
118 ret
= tdb_transaction_commit(tdb
)==0 ? SUCCESS
: OTHER_FAILURE
;
121 if (tdb
->flags
& TDB_VERSION1
)
122 ret
= tdb1_needs_recovery(tdb
) ? SUCCESS
: FAILED
;
124 ret
= tdb_needs_recovery(tdb
) ? SUCCESS
: FAILED
;
127 ret
= tdb_check(tdb
, NULL
, NULL
) == 0 ? SUCCESS
: OTHER_FAILURE
;
130 ret
= tdb_close(tdb
) == 0 ? SUCCESS
: OTHER_FAILURE
;
134 /* We do this async */
141 if (locking_would_block
)
142 ret
= WOULD_HAVE_BLOCKED
;
148 int cmdfd
, responsefd
;
151 /* Do this before doing any tdb stuff. Return handle, or NULL. */
152 struct agent
*prepare_external_agent(void)
155 int command
[2], response
[2];
156 char name
[1+PATH_MAX
];
158 if (pipe(command
) != 0 || pipe(response
) != 0)
166 struct agent
*agent
= malloc(sizeof(*agent
));
170 agent
->cmdfd
= command
[1];
171 agent
->responsefd
= response
[0];
178 /* We want to fail, not block. */
179 nonblocking_locks
= true;
180 log_prefix
= "external: ";
181 while ((ret
= read(command
[0], name
, sizeof(name
))) > 0) {
182 enum agent_return result
;
184 result
= do_operation(name
[0], name
+1);
185 if (write(response
[1], &result
, sizeof(result
))
187 err(1, "Writing response");
188 if (name
[0] == SEND_SIGNAL
) {
189 struct timeval ten_ms
;
191 ten_ms
.tv_usec
= 10000;
192 select(0, NULL
, NULL
, NULL
, &ten_ms
);
193 kill(getppid(), SIGUSR1
);
199 /* Ask the external agent to try to do an operation. */
200 enum agent_return
external_agent_operation(struct agent
*agent
,
204 enum agent_return res
;
210 len
= 1 + strlen(name
) + 1;
211 string
= malloc(len
);
214 strcpy(string
+1, name
);
216 if (write(agent
->cmdfd
, string
, len
) != len
217 || read(agent
->responsefd
, &res
, sizeof(res
)) != sizeof(res
))
224 const char *agent_return_name(enum agent_return ret
)
226 return ret
== SUCCESS
? "SUCCESS"
227 : ret
== WOULD_HAVE_BLOCKED
? "WOULD_HAVE_BLOCKED"
228 : ret
== AGENT_DIED
? "AGENT_DIED"
229 : ret
== FAILED
? "FAILED"
230 : ret
== OTHER_FAILURE
? "OTHER_FAILURE"
234 const char *operation_name(enum operation op
)
237 case OPEN
: return "OPEN";
238 case OPEN_WITH_HOOK
: return "OPEN_WITH_HOOK";
239 case FETCH
: return "FETCH";
240 case STORE
: return "STORE";
241 case CHECK
: return "CHECK";
242 case TRANSACTION_START
: return "TRANSACTION_START";
243 case TRANSACTION_COMMIT
: return "TRANSACTION_COMMIT";
244 case NEEDS_RECOVERY
: return "NEEDS_RECOVERY";
245 case SEND_SIGNAL
: return "SEND_SIGNAL";
246 case CLOSE
: return "CLOSE";
248 return "**INVALID**";
251 void free_external_agent(struct agent
*agent
)
254 close(agent
->responsefd
);