1 #include "external-agent.h"
3 #include "lock-tracking.h"
13 #include "tap-interface.h"
17 static struct tdb_context
*tdb
;
19 void (*external_agent_free
)(void *) = free
;
21 static enum TDB_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 tdb1'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
;
52 union tdb_attribute cif
;
54 if (op
!= OPEN
&& op
!= OPEN_WITH_HOOK
&& !tdb
) {
55 diag("external: No tdb open!");
59 diag("external: %s", operation_name(op
));
61 k
= tdb_mkdata(name
, strlen(name
));
63 locking_would_block
= 0;
67 diag("Already have tdb %s open", tdb_name(tdb
));
70 tdb
= tdb_open(name
, TDB_DEFAULT
, O_RDWR
, 0, &tap_log_attr
);
72 if (!locking_would_block
)
73 diag("Opening tdb gave %s", strerror(errno
));
81 diag("Already have tdb %s open", tdb_name(tdb
));
84 cif
.openhook
.base
.attr
= TDB_ATTRIBUTE_OPENHOOK
;
85 cif
.openhook
.base
.next
= &tap_log_attr
;
86 cif
.openhook
.fn
= clear_if_first
;
87 tdb
= tdb_open(name
, TDB_DEFAULT
, O_RDWR
, 0, &cif
);
89 if (!locking_would_block
)
90 diag("Opening tdb gave %s", strerror(errno
));
97 ecode
= tdb_fetch(tdb
, k
, &data
);
98 if (ecode
== TDB_ERR_NOEXIST
) {
100 } else if (ecode
< 0) {
102 } else if (!tdb_deq(data
, k
)) {
104 external_agent_free(data
.dptr
);
107 external_agent_free(data
.dptr
);
111 ret
= tdb_store(tdb
, k
, k
, 0) == 0 ? SUCCESS
: OTHER_FAILURE
;
113 case TRANSACTION_START
:
114 ret
= tdb_transaction_start(tdb
) == 0 ? SUCCESS
: OTHER_FAILURE
;
116 case TRANSACTION_COMMIT
:
117 ret
= tdb_transaction_commit(tdb
)==0 ? SUCCESS
: OTHER_FAILURE
;
120 ret
= external_agent_needs_rec(tdb
);
123 ret
= tdb_check(tdb
, NULL
, NULL
) == 0 ? SUCCESS
: OTHER_FAILURE
;
126 ret
= tdb_close(tdb
) == 0 ? SUCCESS
: OTHER_FAILURE
;
130 /* We do this async */
137 if (locking_would_block
)
138 ret
= WOULD_HAVE_BLOCKED
;
144 int cmdfd
, responsefd
;
147 /* Do this before doing any tdb stuff. Return handle, or NULL. */
148 struct agent
*prepare_external_agent(void)
151 int command
[2], response
[2];
152 char name
[1+PATH_MAX
];
154 if (pipe(command
) != 0 || pipe(response
) != 0)
162 struct agent
*agent
= malloc(sizeof(*agent
));
166 agent
->cmdfd
= command
[1];
167 agent
->responsefd
= response
[0];
174 /* We want to fail, not block. */
175 nonblocking_locks
= true;
176 log_prefix
= "external: ";
177 while ((ret
= read(command
[0], name
, sizeof(name
))) > 0) {
178 enum agent_return result
;
180 result
= do_operation(name
[0], name
+1);
181 if (write(response
[1], &result
, sizeof(result
))
183 err(1, "Writing response");
184 if (name
[0] == SEND_SIGNAL
) {
185 struct timeval ten_ms
;
187 ten_ms
.tv_usec
= 10000;
188 select(0, NULL
, NULL
, NULL
, &ten_ms
);
189 kill(getppid(), SIGUSR1
);
195 /* Ask the external agent to try to do an operation. */
196 enum agent_return
external_agent_operation(struct agent
*agent
,
200 enum agent_return res
;
206 len
= 1 + strlen(name
) + 1;
207 string
= malloc(len
);
210 strcpy(string
+1, name
);
212 if (write(agent
->cmdfd
, string
, len
) != len
213 || read(agent
->responsefd
, &res
, sizeof(res
)) != sizeof(res
))
220 const char *agent_return_name(enum agent_return ret
)
222 return ret
== SUCCESS
? "SUCCESS"
223 : ret
== WOULD_HAVE_BLOCKED
? "WOULD_HAVE_BLOCKED"
224 : ret
== AGENT_DIED
? "AGENT_DIED"
225 : ret
== FAILED
? "FAILED"
226 : ret
== OTHER_FAILURE
? "OTHER_FAILURE"
230 const char *operation_name(enum operation op
)
233 case OPEN
: return "OPEN";
234 case OPEN_WITH_HOOK
: return "OPEN_WITH_HOOK";
235 case FETCH
: return "FETCH";
236 case STORE
: return "STORE";
237 case CHECK
: return "CHECK";
238 case TRANSACTION_START
: return "TRANSACTION_START";
239 case TRANSACTION_COMMIT
: return "TRANSACTION_COMMIT";
240 case NEEDS_RECOVERY
: return "NEEDS_RECOVERY";
241 case SEND_SIGNAL
: return "SEND_SIGNAL";
242 case CLOSE
: return "CLOSE";
244 return "**INVALID**";
247 void free_external_agent(struct agent
*agent
)
250 close(agent
->responsefd
);