messaging3: Move messaging_hdr handling to messages.c.
[Samba.git] / source3 / lib / messages_dgm.c
blob2c43ec3d8a37a2fbffda61c545ecdecf064be89d
1 /*
2 * Unix SMB/CIFS implementation.
3 * Samba internal messaging functions
4 * Copyright (C) 2013 by Volker Lendecke
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "includes.h"
21 #include "lib/util/data_blob.h"
22 #include "lib/util/debug.h"
23 #include "lib/unix_msg/unix_msg.h"
24 #include "system/filesys.h"
25 #include "lib/messages_dgm.h"
26 #include "lib/param/param.h"
27 #include "poll_funcs/poll_funcs_tevent.h"
28 #include "unix_msg/unix_msg.h"
30 struct messaging_dgm_context {
31 pid_t pid;
32 struct poll_funcs *msg_callbacks;
33 void *tevent_handle;
34 struct unix_msg_ctx *dgm_ctx;
35 char *cache_dir;
36 int lockfile_fd;
38 void (*recv_cb)(const uint8_t *msg,
39 size_t msg_len,
40 void *private_data);
41 void *recv_cb_private_data;
43 bool *have_dgm_context;
46 static void messaging_dgm_recv(struct unix_msg_ctx *ctx,
47 uint8_t *msg, size_t msg_len,
48 void *private_data);
50 static int messaging_dgm_context_destructor(struct messaging_dgm_context *c);
52 static int messaging_dgm_lockfile_create(TALLOC_CTX *tmp_ctx,
53 const char *cache_dir,
54 uid_t dir_owner, pid_t pid,
55 int *plockfile_fd, uint64_t unique)
57 fstring buf;
58 char *dir;
59 char *lockfile_name;
60 int lockfile_fd;
61 struct flock lck;
62 int unique_len, ret;
63 ssize_t written;
64 bool ok;
66 dir = talloc_asprintf(tmp_ctx, "%s/lck", cache_dir);
67 if (dir == NULL) {
68 return ENOMEM;
71 ok = directory_create_or_exist_strict(dir, dir_owner, 0755);
72 if (!ok) {
73 ret = errno;
74 DEBUG(1, ("%s: Could not create lock directory: %s\n",
75 __func__, strerror(ret)));
76 TALLOC_FREE(dir);
77 return ret;
80 lockfile_name = talloc_asprintf(tmp_ctx, "%s/%u", dir,
81 (unsigned)pid);
82 TALLOC_FREE(dir);
83 if (lockfile_name == NULL) {
84 DEBUG(1, ("%s: talloc_asprintf failed\n", __func__));
85 return ENOMEM;
88 /* no O_EXCL, existence check is via the fcntl lock */
90 lockfile_fd = open(lockfile_name, O_NONBLOCK|O_CREAT|O_WRONLY, 0644);
91 if (lockfile_fd == -1) {
92 ret = errno;
93 DEBUG(1, ("%s: open failed: %s\n", __func__, strerror(errno)));
94 goto fail_free;
97 lck = (struct flock) {
98 .l_type = F_WRLCK,
99 .l_whence = SEEK_SET
102 ret = fcntl(lockfile_fd, F_SETLK, &lck);
103 if (ret == -1) {
104 ret = errno;
105 DEBUG(1, ("%s: fcntl failed: %s\n", __func__, strerror(ret)));
106 goto fail_close;
109 unique_len = snprintf(buf, sizeof(buf), "%"PRIu64, unique);
111 /* shorten a potentially preexisting file */
113 ret = ftruncate(lockfile_fd, unique_len);
114 if (ret == -1) {
115 ret = errno;
116 DEBUG(1, ("%s: ftruncate failed: %s\n", __func__,
117 strerror(ret)));
118 goto fail_unlink;
121 written = write(lockfile_fd, buf, unique_len);
122 if (written != unique_len) {
123 ret = errno;
124 DEBUG(1, ("%s: write failed: %s\n", __func__, strerror(ret)));
125 goto fail_unlink;
128 TALLOC_FREE(lockfile_name);
129 *plockfile_fd = lockfile_fd;
130 return 0;
132 fail_unlink:
133 unlink(lockfile_name);
134 fail_close:
135 close(lockfile_fd);
136 fail_free:
137 TALLOC_FREE(lockfile_name);
138 return ret;
141 static int messaging_dgm_lockfile_remove(TALLOC_CTX *tmp_ctx,
142 const char *cache_dir, pid_t pid)
144 char *lockfile_name;
145 int ret;
147 lockfile_name = talloc_asprintf(
148 tmp_ctx, "%s/lck/%u", cache_dir, (unsigned)pid);
149 if (lockfile_name == NULL) {
150 return ENOMEM;
153 ret = unlink(lockfile_name);
154 if (ret == -1) {
155 ret = errno;
156 DEBUG(10, ("%s: unlink(%s) failed: %s\n", __func__,
157 lockfile_name, strerror(ret)));
160 TALLOC_FREE(lockfile_name);
161 return ret;
164 int messaging_dgm_init(TALLOC_CTX *mem_ctx,
165 struct tevent_context *ev,
166 struct server_id pid,
167 const char *cache_dir,
168 uid_t dir_owner,
169 void (*recv_cb)(const uint8_t *msg,
170 size_t msg_len,
171 void *private_data),
172 void *recv_cb_private_data,
173 struct messaging_dgm_context **pctx)
175 struct messaging_dgm_context *ctx;
176 int ret;
177 bool ok;
178 char *socket_dir;
179 struct sockaddr_un socket_address;
180 size_t sockname_len;
181 uint64_t cookie;
182 static bool have_dgm_context = false;
184 if (have_dgm_context) {
185 return EEXIST;
188 ctx = talloc_zero(mem_ctx, struct messaging_dgm_context);
189 if (ctx == NULL) {
190 goto fail_nomem;
192 ctx->pid = pid.pid;
193 ctx->recv_cb = recv_cb;
194 ctx->recv_cb_private_data = recv_cb_private_data;
196 ctx->cache_dir = talloc_strdup(ctx, cache_dir);
197 if (ctx->cache_dir == NULL) {
198 goto fail_nomem;
200 socket_dir = talloc_asprintf(ctx, "%s/msg", cache_dir);
201 if (socket_dir == NULL) {
202 goto fail_nomem;
205 socket_address = (struct sockaddr_un) { .sun_family = AF_UNIX };
206 sockname_len = snprintf(socket_address.sun_path,
207 sizeof(socket_address.sun_path),
208 "%s/%u", socket_dir, (unsigned)pid.pid);
209 if (sockname_len >= sizeof(socket_address.sun_path)) {
210 TALLOC_FREE(ctx);
211 return ENAMETOOLONG;
214 ret = messaging_dgm_lockfile_create(ctx, cache_dir, dir_owner, pid.pid,
215 &ctx->lockfile_fd, pid.unique_id);
216 if (ret != 0) {
217 DEBUG(1, ("%s: messaging_dgm_create_lockfile failed: %s\n",
218 __func__, strerror(ret)));
219 TALLOC_FREE(ctx);
220 return ret;
223 ctx->msg_callbacks = poll_funcs_init_tevent(ctx);
224 if (ctx->msg_callbacks == NULL) {
225 goto fail_nomem;
228 ctx->tevent_handle = poll_funcs_tevent_register(
229 ctx, ctx->msg_callbacks, ev);
230 if (ctx->tevent_handle == NULL) {
231 goto fail_nomem;
234 ok = directory_create_or_exist_strict(socket_dir, dir_owner, 0700);
235 if (!ok) {
236 DEBUG(1, ("Could not create socket directory\n"));
237 TALLOC_FREE(ctx);
238 return EACCES;
240 TALLOC_FREE(socket_dir);
242 unlink(socket_address.sun_path);
244 generate_random_buffer((uint8_t *)&cookie, sizeof(cookie));
246 ret = unix_msg_init(&socket_address, ctx->msg_callbacks, 1024, cookie,
247 messaging_dgm_recv, ctx, &ctx->dgm_ctx);
248 if (ret != 0) {
249 DEBUG(1, ("unix_msg_init failed: %s\n", strerror(ret)));
250 TALLOC_FREE(ctx);
251 return ret;
253 talloc_set_destructor(ctx, messaging_dgm_context_destructor);
255 ctx->have_dgm_context = &have_dgm_context;
257 *pctx = ctx;
258 return 0;
260 fail_nomem:
261 TALLOC_FREE(ctx);
262 return ENOMEM;
265 static int messaging_dgm_context_destructor(struct messaging_dgm_context *c)
268 * First delete the socket to avoid races. The lockfile is the
269 * indicator that we're still around.
271 unix_msg_free(c->dgm_ctx);
273 if (getpid() == c->pid) {
274 (void)messaging_dgm_lockfile_remove(c, c->cache_dir, c->pid);
276 close(c->lockfile_fd);
278 if (c->have_dgm_context != NULL) {
279 *c->have_dgm_context = false;
282 return 0;
285 int messaging_dgm_send(struct messaging_dgm_context *ctx, pid_t pid,
286 const struct iovec *iov, int iovlen)
288 struct sockaddr_un dst;
289 ssize_t dst_pathlen;
290 int ret;
292 dst = (struct sockaddr_un) { .sun_family = AF_UNIX };
294 dst_pathlen = snprintf(dst.sun_path, sizeof(dst.sun_path),
295 "%s/msg/%u", ctx->cache_dir, (unsigned)pid);
296 if (dst_pathlen >= sizeof(dst.sun_path)) {
297 return ENAMETOOLONG;
300 DEBUG(10, ("%s: Sending message to %u\n", __func__, (unsigned)pid));
302 ret = unix_msg_send(ctx->dgm_ctx, &dst, iov, iovlen);
304 return ret;
307 static void messaging_dgm_recv(struct unix_msg_ctx *ctx,
308 uint8_t *msg, size_t msg_len,
309 void *private_data)
311 struct messaging_dgm_context *dgm_ctx = talloc_get_type_abort(
312 private_data, struct messaging_dgm_context);
314 dgm_ctx->recv_cb(msg, msg_len, dgm_ctx->recv_cb_private_data);
317 int messaging_dgm_cleanup(struct messaging_dgm_context *ctx, pid_t pid)
319 char *lockfile_name, *socket_name;
320 int fd, ret;
321 struct flock lck = {};
323 lockfile_name = talloc_asprintf(talloc_tos(), "%s/lck/%u",
324 ctx->cache_dir, (unsigned)pid);
325 if (lockfile_name == NULL) {
326 return ENOMEM;
328 socket_name = talloc_asprintf(lockfile_name, "%s/msg/%u",
329 ctx->cache_dir, (unsigned)pid);
330 if (socket_name == NULL) {
331 TALLOC_FREE(lockfile_name);
332 return ENOMEM;
335 fd = open(lockfile_name, O_NONBLOCK|O_WRONLY, 0);
336 if (fd == -1) {
337 ret = errno;
338 DEBUG(10, ("%s: open(%s) failed: %s\n", __func__,
339 lockfile_name, strerror(ret)));
340 TALLOC_FREE(lockfile_name);
341 return ret;
344 lck.l_type = F_WRLCK;
345 lck.l_whence = SEEK_SET;
346 lck.l_start = 0;
347 lck.l_len = 0;
349 ret = fcntl(fd, F_SETLK, &lck);
350 if (ret != 0) {
351 ret = errno;
352 DEBUG(10, ("%s: Could not get lock: %s\n", __func__,
353 strerror(ret)));
354 TALLOC_FREE(lockfile_name);
355 close(fd);
356 return ret;
359 (void)unlink(socket_name);
360 (void)unlink(lockfile_name);
361 (void)close(fd);
363 TALLOC_FREE(lockfile_name);
364 return 0;
367 int messaging_dgm_wipe(struct messaging_dgm_context *ctx)
369 char *msgdir_name;
370 DIR *msgdir;
371 struct dirent *dp;
372 pid_t our_pid = getpid();
373 int ret;
376 * We scan the socket directory and not the lock directory. Otherwise
377 * we would race against messaging_dgm_lockfile_create's open(O_CREAT)
378 * and fcntl(SETLK).
381 msgdir_name = talloc_asprintf(talloc_tos(), "%s/msg", ctx->cache_dir);
382 if (msgdir_name == NULL) {
383 return ENOMEM;
386 msgdir = opendir(msgdir_name);
387 if (msgdir == NULL) {
388 ret = errno;
389 TALLOC_FREE(msgdir_name);
390 return ret;
392 TALLOC_FREE(msgdir_name);
394 while ((dp = readdir(msgdir)) != NULL) {
395 unsigned long pid;
397 pid = strtoul(dp->d_name, NULL, 10);
398 if (pid == 0) {
400 * . and .. and other malformed entries
402 continue;
404 if (pid == our_pid) {
406 * fcntl(F_GETLK) will succeed for ourselves, we hold
407 * that lock ourselves.
409 continue;
412 ret = messaging_dgm_cleanup(ctx, pid);
413 DEBUG(10, ("messaging_dgm_cleanup(%lu) returned %s\n",
414 pid, ret ? strerror(ret) : "ok"));
416 closedir(msgdir);
418 return 0;
421 void *messaging_dgm_register_tevent_context(TALLOC_CTX *mem_ctx,
422 struct messaging_dgm_context *ctx,
423 struct tevent_context *ev)
425 return poll_funcs_tevent_register(mem_ctx, ctx->msg_callbacks, ev);