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/>.
21 #include "system/network.h"
22 #include "system/filesys.h"
24 #include "lib/util/data_blob.h"
25 #include "lib/util/debug.h"
26 #include "lib/unix_msg/unix_msg.h"
27 #include "system/filesys.h"
28 #include "lib/messages_dgm.h"
29 #include "lib/param/param.h"
30 #include "poll_funcs/poll_funcs_tevent.h"
31 #include "unix_msg/unix_msg.h"
32 #include "lib/util/genrand.h"
36 * This will carry enough for a socket path
38 char buf
[sizeof(struct sockaddr_un
)];
41 struct messaging_dgm_context
{
43 struct poll_funcs
*msg_callbacks
;
45 struct unix_msg_ctx
*dgm_ctx
;
46 struct sun_path_buf socket_dir
;
47 struct sun_path_buf lockfile_dir
;
50 void (*recv_cb
)(const uint8_t *msg
,
55 void *recv_cb_private_data
;
57 bool *have_dgm_context
;
60 static struct messaging_dgm_context
*global_dgm_context
;
62 static void messaging_dgm_recv(struct unix_msg_ctx
*ctx
,
63 uint8_t *msg
, size_t msg_len
,
64 int *fds
, size_t num_fds
,
67 static int messaging_dgm_context_destructor(struct messaging_dgm_context
*c
);
69 static int messaging_dgm_lockfile_create(struct messaging_dgm_context
*ctx
,
70 pid_t pid
, int *plockfile_fd
,
75 struct sun_path_buf lockfile_name
;
81 ret
= snprintf(lockfile_name
.buf
, sizeof(lockfile_name
.buf
),
82 "%s/%u", ctx
->lockfile_dir
.buf
, (unsigned)pid
);
83 if (ret
>= sizeof(lockfile_name
.buf
)) {
87 /* no O_EXCL, existence check is via the fcntl lock */
89 lockfile_fd
= open(lockfile_name
.buf
, O_NONBLOCK
|O_CREAT
|O_RDWR
,
92 if ((lockfile_fd
== -1) &&
93 ((errno
== ENXIO
) /* Linux */ ||
94 (errno
== ENODEV
) /* Linux kernel bug */ ||
95 (errno
== EOPNOTSUPP
) /* FreeBSD */)) {
97 * Huh -- a socket? This might be a stale socket from
98 * an upgrade of Samba. Just unlink and retry, nobody
99 * else is supposed to be here at this time.
101 * Yes, this is racy, but I don't see a way to deal
102 * with this properly.
104 unlink(lockfile_name
.buf
);
106 lockfile_fd
= open(lockfile_name
.buf
,
107 O_NONBLOCK
|O_CREAT
|O_WRONLY
,
111 if (lockfile_fd
== -1) {
113 DEBUG(1, ("%s: open failed: %s\n", __func__
, strerror(errno
)));
117 lck
= (struct flock
) {
122 ret
= fcntl(lockfile_fd
, F_SETLK
, &lck
);
125 DEBUG(1, ("%s: fcntl failed: %s\n", __func__
, strerror(ret
)));
130 * Directly using the binary value for
131 * SERVERID_UNIQUE_ID_NOT_TO_VERIFY is a layering
132 * violation. But including all of ndr here just for this
133 * seems to be a bit overkill to me. Also, messages_dgm might
134 * be replaced sooner or later by something streams-based,
135 * where unique_id generation will be handled differently.
139 generate_random_buffer((uint8_t *)&unique
, sizeof(unique
));
140 } while (unique
== UINT64_C(0xFFFFFFFFFFFFFFFF));
142 unique_len
= snprintf(buf
, sizeof(buf
), "%ju\n", (uintmax_t)unique
);
144 /* shorten a potentially preexisting file */
146 ret
= ftruncate(lockfile_fd
, unique_len
);
149 DEBUG(1, ("%s: ftruncate failed: %s\n", __func__
,
154 written
= write(lockfile_fd
, buf
, unique_len
);
155 if (written
!= unique_len
) {
157 DEBUG(1, ("%s: write failed: %s\n", __func__
, strerror(ret
)));
161 *plockfile_fd
= lockfile_fd
;
166 unlink(lockfile_name
.buf
);
172 int messaging_dgm_init(struct tevent_context
*ev
,
174 const char *socket_dir
,
175 const char *lockfile_dir
,
176 void (*recv_cb
)(const uint8_t *msg
,
181 void *recv_cb_private_data
)
183 struct messaging_dgm_context
*ctx
;
185 struct sockaddr_un socket_address
;
187 static bool have_dgm_context
= false;
189 if (have_dgm_context
) {
193 ctx
= talloc_zero(NULL
, struct messaging_dgm_context
);
198 ctx
->recv_cb
= recv_cb
;
199 ctx
->recv_cb_private_data
= recv_cb_private_data
;
201 len
= strlcpy(ctx
->lockfile_dir
.buf
, lockfile_dir
,
202 sizeof(ctx
->lockfile_dir
.buf
));
203 if (len
>= sizeof(ctx
->lockfile_dir
.buf
)) {
208 len
= strlcpy(ctx
->socket_dir
.buf
, socket_dir
,
209 sizeof(ctx
->socket_dir
.buf
));
210 if (len
>= sizeof(ctx
->socket_dir
.buf
)) {
215 socket_address
= (struct sockaddr_un
) { .sun_family
= AF_UNIX
};
216 len
= snprintf(socket_address
.sun_path
,
217 sizeof(socket_address
.sun_path
),
218 "%s/%u", socket_dir
, (unsigned)ctx
->pid
);
219 if (len
>= sizeof(socket_address
.sun_path
)) {
224 ret
= messaging_dgm_lockfile_create(ctx
, ctx
->pid
, &ctx
->lockfile_fd
,
227 DEBUG(1, ("%s: messaging_dgm_create_lockfile failed: %s\n",
228 __func__
, strerror(ret
)));
233 ctx
->msg_callbacks
= poll_funcs_init_tevent(ctx
);
234 if (ctx
->msg_callbacks
== NULL
) {
238 ctx
->tevent_handle
= poll_funcs_tevent_register(
239 ctx
, ctx
->msg_callbacks
, ev
);
240 if (ctx
->tevent_handle
== NULL
) {
244 unlink(socket_address
.sun_path
);
246 ret
= unix_msg_init(&socket_address
, ctx
->msg_callbacks
, 1024,
247 messaging_dgm_recv
, ctx
, &ctx
->dgm_ctx
);
249 DEBUG(1, ("unix_msg_init failed: %s\n", strerror(ret
)));
253 talloc_set_destructor(ctx
, messaging_dgm_context_destructor
);
255 ctx
->have_dgm_context
= &have_dgm_context
;
257 global_dgm_context
= ctx
;
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 struct sun_path_buf name
;
277 ret
= snprintf(name
.buf
, sizeof(name
.buf
), "%s/%u",
278 c
->lockfile_dir
.buf
, (unsigned)c
->pid
);
279 if (ret
>= sizeof(name
.buf
)) {
281 * We've checked the length when creating, so this
282 * should never happen
288 close(c
->lockfile_fd
);
290 if (c
->have_dgm_context
!= NULL
) {
291 *c
->have_dgm_context
= false;
297 void messaging_dgm_destroy(void)
299 TALLOC_FREE(global_dgm_context
);
302 int messaging_dgm_send(pid_t pid
,
303 const struct iovec
*iov
, int iovlen
,
304 const int *fds
, size_t num_fds
)
306 struct messaging_dgm_context
*ctx
= global_dgm_context
;
307 struct sockaddr_un dst
;
315 dst
= (struct sockaddr_un
) { .sun_family
= AF_UNIX
};
317 dst_pathlen
= snprintf(dst
.sun_path
, sizeof(dst
.sun_path
),
318 "%s/%u", ctx
->socket_dir
.buf
, (unsigned)pid
);
319 if (dst_pathlen
>= sizeof(dst
.sun_path
)) {
323 DEBUG(10, ("%s: Sending message to %u\n", __func__
, (unsigned)pid
));
325 ret
= unix_msg_send(ctx
->dgm_ctx
, &dst
, iov
, iovlen
, fds
, num_fds
);
330 static void messaging_dgm_recv(struct unix_msg_ctx
*ctx
,
331 uint8_t *msg
, size_t msg_len
,
332 int *fds
, size_t num_fds
,
335 struct messaging_dgm_context
*dgm_ctx
= talloc_get_type_abort(
336 private_data
, struct messaging_dgm_context
);
338 dgm_ctx
->recv_cb(msg
, msg_len
, fds
, num_fds
,
339 dgm_ctx
->recv_cb_private_data
);
342 static int messaging_dgm_read_unique(int fd
, uint64_t *punique
)
346 unsigned long long unique
;
349 rw_ret
= pread(fd
, buf
, sizeof(buf
)-1, 0);
355 unique
= strtoull(buf
, &endptr
, 10);
356 if ((unique
== 0) && (errno
== EINVAL
)) {
359 if ((unique
== ULLONG_MAX
) && (errno
== ERANGE
)) {
362 if (endptr
[0] != '\n') {
369 int messaging_dgm_get_unique(pid_t pid
, uint64_t *unique
)
371 struct messaging_dgm_context
*ctx
= global_dgm_context
;
372 struct sun_path_buf lockfile_name
;
379 if (pid
== getpid()) {
381 * Protect against losing our own lock
383 return messaging_dgm_read_unique(ctx
->lockfile_fd
, unique
);
386 ret
= snprintf(lockfile_name
.buf
, sizeof(lockfile_name
.buf
),
387 "%s/%u", ctx
->lockfile_dir
.buf
, (int)pid
);
388 if (ret
>= sizeof(lockfile_name
.buf
)) {
392 fd
= open(lockfile_name
.buf
, O_NONBLOCK
|O_RDONLY
, 0);
397 ret
= messaging_dgm_read_unique(fd
, unique
);
402 int messaging_dgm_cleanup(pid_t pid
)
404 struct messaging_dgm_context
*ctx
= global_dgm_context
;
405 struct sun_path_buf lockfile_name
, socket_name
;
407 struct flock lck
= {};
413 len
= snprintf(socket_name
.buf
, sizeof(socket_name
.buf
), "%s/%u",
414 ctx
->socket_dir
.buf
, (unsigned)pid
);
415 if (len
>= sizeof(socket_name
.buf
)) {
419 len
= snprintf(lockfile_name
.buf
, sizeof(lockfile_name
.buf
), "%s/%u",
420 ctx
->lockfile_dir
.buf
, (unsigned)pid
);
421 if (len
>= sizeof(lockfile_name
.buf
)) {
425 fd
= open(lockfile_name
.buf
, O_NONBLOCK
|O_WRONLY
, 0);
429 DEBUG(10, ("%s: open(%s) failed: %s\n", __func__
,
430 lockfile_name
.buf
, strerror(ret
)));
435 lck
.l_type
= F_WRLCK
;
436 lck
.l_whence
= SEEK_SET
;
440 ret
= fcntl(fd
, F_SETLK
, &lck
);
443 if ((ret
!= EACCES
) && (ret
!= EAGAIN
)) {
444 DEBUG(10, ("%s: Could not get lock: %s\n", __func__
,
451 DEBUG(10, ("%s: Cleaning up : %s\n", __func__
, strerror(ret
)));
453 (void)unlink(socket_name
.buf
);
454 (void)unlink(lockfile_name
.buf
);
459 int messaging_dgm_wipe(void)
461 struct messaging_dgm_context
*ctx
= global_dgm_context
;
464 pid_t our_pid
= getpid();
472 * We scan the socket directory and not the lock directory. Otherwise
473 * we would race against messaging_dgm_lockfile_create's open(O_CREAT)
477 msgdir
= opendir(ctx
->socket_dir
.buf
);
478 if (msgdir
== NULL
) {
482 while ((dp
= readdir(msgdir
)) != NULL
) {
485 pid
= strtoul(dp
->d_name
, NULL
, 10);
488 * . and .. and other malformed entries
492 if (pid
== our_pid
) {
494 * fcntl(F_GETLK) will succeed for ourselves, we hold
495 * that lock ourselves.
500 ret
= messaging_dgm_cleanup(pid
);
501 DEBUG(10, ("messaging_dgm_cleanup(%lu) returned %s\n",
502 pid
, ret
? strerror(ret
) : "ok"));
509 void *messaging_dgm_register_tevent_context(TALLOC_CTX
*mem_ctx
,
510 struct tevent_context
*ev
)
512 struct messaging_dgm_context
*ctx
= global_dgm_context
;
517 return poll_funcs_tevent_register(mem_ctx
, ctx
->msg_callbacks
, ev
);