]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-daemon/sd-daemon.c
log: minimize includes in log.h
[thirdparty/systemd.git] / src / libsystemd / sd-daemon / sd-daemon.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
abbbea81 2/***
0ebee881
KS
3 This file is part of systemd.
4
abbbea81
LP
5 Copyright 2010 Lennart Poettering
6
0ebee881
KS
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
abbbea81 16
0ebee881
KS
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
8c47c732 20
abbbea81 21#include <errno.h>
916abb21 22#include <limits.h>
0ebee881 23#include <mqueue.h>
8dd4c05b
LP
24#include <netinet/in.h>
25#include <stdarg.h>
26#include <stddef.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <sys/socket.h>
31#include <sys/stat.h>
32#include <sys/un.h>
33#include <unistd.h>
abbbea81 34
07630cea
LP
35#include "sd-daemon.h"
36
b5efdb8a 37#include "alloc-util.h"
3ffd4af2 38#include "fd-util.h"
f4f15635 39#include "fs-util.h"
6bedfcbb 40#include "parse-util.h"
be8f4e9e 41#include "path-util.h"
dccca82b 42#include "process-util.h"
3cb46740 43#include "socket-util.h"
8dd4c05b
LP
44#include "strv.h"
45#include "util.h"
46
a47806fa
LP
47#define SNDBUF_SIZE (8*1024*1024)
48
8dd4c05b
LP
49static void unsetenv_all(bool unset_environment) {
50
51 if (!unset_environment)
52 return;
53
54 unsetenv("LISTEN_PID");
55 unsetenv("LISTEN_FDS");
56 unsetenv("LISTEN_FDNAMES");
57}
58
0ebee881 59_public_ int sd_listen_fds(int unset_environment) {
abbbea81 60 const char *e;
046c93f8 61 int n, r, fd;
be8f4e9e 62 pid_t pid;
abbbea81 63
50425d16
MS
64 e = getenv("LISTEN_PID");
65 if (!e) {
abbbea81
LP
66 r = 0;
67 goto finish;
68 }
69
be8f4e9e
LP
70 r = parse_pid(e, &pid);
71 if (r < 0)
abbbea81 72 goto finish;
abbbea81
LP
73
74 /* Is this for us? */
df0ff127 75 if (getpid_cached() != pid) {
abbbea81
LP
76 r = 0;
77 goto finish;
78 }
79
50425d16
MS
80 e = getenv("LISTEN_FDS");
81 if (!e) {
abbbea81
LP
82 r = 0;
83 goto finish;
84 }
85
046c93f8 86 r = safe_atoi(e, &n);
be8f4e9e 87 if (r < 0)
abbbea81 88 goto finish;
8640e111 89
046c93f8
VC
90 assert_cc(SD_LISTEN_FDS_START < INT_MAX);
91 if (n <= 0 || n > INT_MAX - SD_LISTEN_FDS_START) {
92 r = -EINVAL;
93 goto finish;
94 }
95
96 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++) {
be8f4e9e
LP
97 r = fd_cloexec(fd, true);
98 if (r < 0)
8640e111 99 goto finish;
8640e111
LP
100 }
101
046c93f8 102 r = n;
abbbea81
LP
103
104finish:
8dd4c05b
LP
105 unsetenv_all(unset_environment);
106 return r;
107}
108
109_public_ int sd_listen_fds_with_names(int unset_environment, char ***names) {
110 _cleanup_strv_free_ char **l = NULL;
111 bool have_names;
112 int n_names = 0, n_fds;
113 const char *e;
114 int r;
115
116 if (!names)
117 return sd_listen_fds(unset_environment);
118
119 e = getenv("LISTEN_FDNAMES");
120 if (e) {
121 n_names = strv_split_extract(&l, e, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
122 if (n_names < 0) {
123 unsetenv_all(unset_environment);
124 return n_names;
125 }
126
127 have_names = true;
128 } else
129 have_names = false;
130
131 n_fds = sd_listen_fds(unset_environment);
132 if (n_fds <= 0)
133 return n_fds;
134
135 if (have_names) {
136 if (n_names != n_fds)
137 return -EINVAL;
138 } else {
139 r = strv_extend_n(&l, "unknown", n_fds);
140 if (r < 0)
141 return r;
abbbea81
LP
142 }
143
8dd4c05b
LP
144 *names = l;
145 l = NULL;
146
147 return n_fds;
abbbea81 148}
7c394faa 149
0ebee881 150_public_ int sd_is_fifo(int fd, const char *path) {
7c394faa
LP
151 struct stat st_fd;
152
e6803801 153 assert_return(fd >= 0, -EBADF);
7c394faa 154
7c394faa
LP
155 if (fstat(fd, &st_fd) < 0)
156 return -errno;
157
158 if (!S_ISFIFO(st_fd.st_mode))
159 return 0;
160
161 if (path) {
162 struct stat st_path;
163
fd8bccfb 164 if (stat(path, &st_path) < 0) {
7c394faa 165
945c2931 166 if (IN_SET(errno, ENOENT, ENOTDIR))
7c394faa
LP
167 return 0;
168
169 return -errno;
170 }
171
172 return
173 st_path.st_dev == st_fd.st_dev &&
174 st_path.st_ino == st_fd.st_ino;
175 }
176
177 return 1;
178}
179
0ebee881 180_public_ int sd_is_special(int fd, const char *path) {
4160ec67
WD
181 struct stat st_fd;
182
e6803801 183 assert_return(fd >= 0, -EBADF);
4160ec67
WD
184
185 if (fstat(fd, &st_fd) < 0)
186 return -errno;
187
188 if (!S_ISREG(st_fd.st_mode) && !S_ISCHR(st_fd.st_mode))
189 return 0;
190
191 if (path) {
192 struct stat st_path;
193
194 if (stat(path, &st_path) < 0) {
195
945c2931 196 if (IN_SET(errno, ENOENT, ENOTDIR))
4160ec67
WD
197 return 0;
198
199 return -errno;
200 }
201
202 if (S_ISREG(st_fd.st_mode) && S_ISREG(st_path.st_mode))
203 return
204 st_path.st_dev == st_fd.st_dev &&
205 st_path.st_ino == st_fd.st_ino;
206 else if (S_ISCHR(st_fd.st_mode) && S_ISCHR(st_path.st_mode))
207 return st_path.st_rdev == st_fd.st_rdev;
208 else
209 return 0;
210 }
211
212 return 1;
213}
214
e6a3081a 215static int sd_is_socket_internal(int fd, int type, int listening) {
7c394faa
LP
216 struct stat st_fd;
217
e6803801 218 assert_return(fd >= 0, -EBADF);
be8f4e9e 219 assert_return(type >= 0, -EINVAL);
7c394faa
LP
220
221 if (fstat(fd, &st_fd) < 0)
222 return -errno;
223
224 if (!S_ISSOCK(st_fd.st_mode))
225 return 0;
226
227 if (type != 0) {
228 int other_type = 0;
229 socklen_t l = sizeof(other_type);
230
231 if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0)
232 return -errno;
233
234 if (l != sizeof(other_type))
235 return -EINVAL;
236
237 if (other_type != type)
238 return 0;
239 }
240
241 if (listening >= 0) {
242 int accepting = 0;
243 socklen_t l = sizeof(accepting);
244
245 if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0)
246 return -errno;
247
248 if (l != sizeof(accepting))
249 return -EINVAL;
250
dde770cf 251 if (!accepting != !listening)
7c394faa
LP
252 return 0;
253 }
254
255 return 1;
256}
257
0ebee881 258_public_ int sd_is_socket(int fd, int family, int type, int listening) {
88ce42f6
LP
259 int r;
260
e6803801 261 assert_return(fd >= 0, -EBADF);
be8f4e9e 262 assert_return(family >= 0, -EINVAL);
88ce42f6 263
50425d16
MS
264 r = sd_is_socket_internal(fd, type, listening);
265 if (r <= 0)
88ce42f6
LP
266 return r;
267
268 if (family > 0) {
1c633045
ZJS
269 union sockaddr_union sockaddr = {};
270 socklen_t l = sizeof(sockaddr);
88ce42f6
LP
271
272 if (getsockname(fd, &sockaddr.sa, &l) < 0)
273 return -errno;
274
b7f42664 275 if (l < sizeof(sa_family_t))
88ce42f6
LP
276 return -EINVAL;
277
278 return sockaddr.sa.sa_family == family;
279 }
280
281 return 1;
282}
283
0ebee881 284_public_ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
1c633045
ZJS
285 union sockaddr_union sockaddr = {};
286 socklen_t l = sizeof(sockaddr);
7c394faa
LP
287 int r;
288
e6803801 289 assert_return(fd >= 0, -EBADF);
be8f4e9e 290 assert_return(IN_SET(family, 0, AF_INET, AF_INET6), -EINVAL);
88ce42f6 291
50425d16
MS
292 r = sd_is_socket_internal(fd, type, listening);
293 if (r <= 0)
7c394faa
LP
294 return r;
295
7c394faa
LP
296 if (getsockname(fd, &sockaddr.sa, &l) < 0)
297 return -errno;
298
b7f42664 299 if (l < sizeof(sa_family_t))
7c394faa
LP
300 return -EINVAL;
301
945c2931 302 if (!IN_SET(sockaddr.sa.sa_family, AF_INET, AF_INET6))
7c394faa
LP
303 return 0;
304
be8f4e9e 305 if (family != 0)
88ce42f6
LP
306 if (sockaddr.sa.sa_family != family)
307 return 0;
308
7c394faa 309 if (port > 0) {
dfde7e8c 310 unsigned sa_port;
7c394faa 311
dfde7e8c
LP
312 r = sockaddr_port(&sockaddr.sa, &sa_port);
313 if (r < 0)
314 return r;
7c394faa 315
dfde7e8c 316 return port == sa_port;
7c394faa
LP
317 }
318
319 return 1;
320}
321
f6f372d2
ZJS
322_public_ int sd_is_socket_sockaddr(int fd, int type, const struct sockaddr* addr, unsigned addr_len, int listening) {
323 union sockaddr_union sockaddr = {};
324 socklen_t l = sizeof(sockaddr);
325 int r;
326
327 assert_return(fd >= 0, -EBADF);
328 assert_return(addr, -EINVAL);
329 assert_return(addr_len >= sizeof(sa_family_t), -ENOBUFS);
330 assert_return(IN_SET(addr->sa_family, AF_INET, AF_INET6), -EPFNOSUPPORT);
331
332 r = sd_is_socket_internal(fd, type, listening);
333 if (r <= 0)
334 return r;
335
336 if (getsockname(fd, &sockaddr.sa, &l) < 0)
337 return -errno;
338
339 if (l < sizeof(sa_family_t))
340 return -EINVAL;
341
342 if (sockaddr.sa.sa_family != addr->sa_family)
343 return 0;
344
345 if (sockaddr.sa.sa_family == AF_INET) {
346 const struct sockaddr_in *in = (const struct sockaddr_in *) addr;
347
348 if (l < sizeof(struct sockaddr_in) || addr_len < sizeof(struct sockaddr_in))
349 return -EINVAL;
350
351 if (in->sin_port != 0 &&
352 sockaddr.in.sin_port != in->sin_port)
353 return false;
354
355 return sockaddr.in.sin_addr.s_addr == in->sin_addr.s_addr;
356
357 } else {
358 const struct sockaddr_in6 *in = (const struct sockaddr_in6 *) addr;
359
360 if (l < sizeof(struct sockaddr_in6) || addr_len < sizeof(struct sockaddr_in6))
361 return -EINVAL;
362
363 if (in->sin6_port != 0 &&
364 sockaddr.in6.sin6_port != in->sin6_port)
365 return false;
366
367 if (in->sin6_flowinfo != 0 &&
368 sockaddr.in6.sin6_flowinfo != in->sin6_flowinfo)
369 return false;
370
371 if (in->sin6_scope_id != 0 &&
372 sockaddr.in6.sin6_scope_id != in->sin6_scope_id)
373 return false;
374
375 return memcmp(sockaddr.in6.sin6_addr.s6_addr, in->sin6_addr.s6_addr,
376 sizeof(in->sin6_addr.s6_addr)) == 0;
377 }
378}
379
0ebee881 380_public_ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
1c633045
ZJS
381 union sockaddr_union sockaddr = {};
382 socklen_t l = sizeof(sockaddr);
7c394faa
LP
383 int r;
384
e6803801 385 assert_return(fd >= 0, -EBADF);
be8f4e9e 386
50425d16
MS
387 r = sd_is_socket_internal(fd, type, listening);
388 if (r <= 0)
7c394faa
LP
389 return r;
390
7c394faa
LP
391 if (getsockname(fd, &sockaddr.sa, &l) < 0)
392 return -errno;
393
b7f42664 394 if (l < sizeof(sa_family_t))
7c394faa
LP
395 return -EINVAL;
396
397 if (sockaddr.sa.sa_family != AF_UNIX)
398 return 0;
399
400 if (path) {
d1d7caee 401 if (length == 0)
7c394faa
LP
402 length = strlen(path);
403
d1d7caee 404 if (length == 0)
7c394faa 405 /* Unnamed socket */
0e098b15 406 return l == offsetof(struct sockaddr_un, sun_path);
7c394faa 407
7c394faa
LP
408 if (path[0])
409 /* Normal path socket */
cd250a39 410 return
0e098b15 411 (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) &&
cd250a39 412 memcmp(path, sockaddr.un.sun_path, length+1) == 0;
7c394faa
LP
413 else
414 /* Abstract namespace socket */
cd250a39 415 return
0e098b15 416 (l == offsetof(struct sockaddr_un, sun_path) + length) &&
cd250a39 417 memcmp(path, sockaddr.un.sun_path, length) == 0;
7c394faa
LP
418 }
419
420 return 1;
421}
8c47c732 422
0ebee881 423_public_ int sd_is_mq(int fd, const char *path) {
916abb21
LP
424 struct mq_attr attr;
425
0260d1d5
ZJS
426 /* Check that the fd is valid */
427 assert_return(fcntl(fd, F_GETFD) >= 0, -errno);
916abb21 428
0260d1d5
ZJS
429 if (mq_getattr(fd, &attr) < 0) {
430 if (errno == EBADF)
431 /* A non-mq fd (or an invalid one, but we ruled that out above) */
432 return 0;
916abb21 433 return -errno;
0260d1d5 434 }
916abb21
LP
435
436 if (path) {
437 char fpath[PATH_MAX];
438 struct stat a, b;
439
be8f4e9e 440 assert_return(path_is_absolute(path), -EINVAL);
916abb21
LP
441
442 if (fstat(fd, &a) < 0)
443 return -errno;
444
445 strncpy(stpcpy(fpath, "/dev/mqueue"), path, sizeof(fpath) - 12);
446 fpath[sizeof(fpath)-1] = 0;
447
448 if (stat(fpath, &b) < 0)
449 return -errno;
450
451 if (a.st_dev != b.st_dev ||
452 a.st_ino != b.st_ino)
453 return 0;
454 }
455
456 return 1;
916abb21
LP
457}
458
a354329f
LP
459_public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char *state, const int *fds, unsigned n_fds) {
460 union sockaddr_union sockaddr = {
461 .sa.sa_family = AF_UNIX,
462 };
463 struct iovec iovec = {
464 .iov_base = (char*) state,
465 };
466 struct msghdr msghdr = {
467 .msg_iov = &iovec,
468 .msg_iovlen = 1,
469 .msg_name = &sockaddr,
470 };
a354329f
LP
471 _cleanup_close_ int fd = -1;
472 struct cmsghdr *cmsg = NULL;
473 const char *e;
64144440 474 bool have_pid;
be8f4e9e 475 int r;
8c47c732
LP
476
477 if (!state) {
478 r = -EINVAL;
479 goto finish;
480 }
481
a354329f
LP
482 if (n_fds > 0 && !fds) {
483 r = -EINVAL;
484 goto finish;
485 }
486
50425d16
MS
487 e = getenv("NOTIFY_SOCKET");
488 if (!e)
08bfb810 489 return 0;
8c47c732
LP
490
491 /* Must be an abstract socket, or an absolute path */
945c2931 492 if (!IN_SET(e[0], '@', '/') || e[1] == 0) {
8c47c732
LP
493 r = -EINVAL;
494 goto finish;
495 }
496
638b56cd
LP
497 if (strlen(e) > sizeof(sockaddr.un.sun_path)) {
498 r = -EINVAL;
499 goto finish;
500 }
501
50425d16
MS
502 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
503 if (fd < 0) {
8c47c732
LP
504 r = -errno;
505 goto finish;
506 }
507
a47806fa
LP
508 fd_inc_sndbuf(fd, SNDBUF_SIZE);
509
a354329f 510 iovec.iov_len = strlen(state);
8c47c732 511
a354329f 512 strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
8c47c732
LP
513 if (sockaddr.un.sun_path[0] == '@')
514 sockaddr.un.sun_path[0] = 0;
515
fc2fffe7 516 msghdr.msg_namelen = SOCKADDR_UN_LEN(sockaddr.un);
a013bd94 517
df0ff127 518 have_pid = pid != 0 && pid != getpid_cached();
d4a144fa 519
64144440 520 if (n_fds > 0 || have_pid) {
96d49011 521 /* CMSG_SPACE(0) may return value different than zero, which results in miscalculated controllen. */
c463a6f1
LP
522 msghdr.msg_controllen =
523 (n_fds > 0 ? CMSG_SPACE(sizeof(int) * n_fds) : 0) +
524 (have_pid ? CMSG_SPACE(sizeof(struct ucred)) : 0);
525
40f44238 526 msghdr.msg_control = alloca0(msghdr.msg_controllen);
a354329f
LP
527
528 cmsg = CMSG_FIRSTHDR(&msghdr);
64144440
ZJS
529 if (n_fds > 0) {
530 cmsg->cmsg_level = SOL_SOCKET;
531 cmsg->cmsg_type = SCM_RIGHTS;
532 cmsg->cmsg_len = CMSG_LEN(sizeof(int) * n_fds);
a354329f 533
64144440 534 memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * n_fds);
be8f4e9e 535
64144440
ZJS
536 if (have_pid)
537 assert_se(cmsg = CMSG_NXTHDR(&msghdr, cmsg));
538 }
a354329f 539
64144440
ZJS
540 if (have_pid) {
541 struct ucred *ucred;
be8f4e9e 542
64144440
ZJS
543 cmsg->cmsg_level = SOL_SOCKET;
544 cmsg->cmsg_type = SCM_CREDENTIALS;
545 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
be8f4e9e 546
64144440
ZJS
547 ucred = (struct ucred*) CMSG_DATA(cmsg);
548 ucred->pid = pid;
549 ucred->uid = getuid();
550 ucred->gid = getgid();
551 }
be8f4e9e
LP
552 }
553
554 /* First try with fake ucred data, as requested */
555 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) {
556 r = 1;
8c47c732
LP
557 goto finish;
558 }
559
a354329f 560 /* If that failed, try with our own ucred instead */
64144440
ZJS
561 if (have_pid) {
562 msghdr.msg_controllen -= CMSG_SPACE(sizeof(struct ucred));
563 if (msghdr.msg_controllen == 0)
a354329f 564 msghdr.msg_control = NULL;
be8f4e9e
LP
565
566 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) {
567 r = 1;
568 goto finish;
569 }
570 }
571
572 r = -errno;
8c47c732
LP
573
574finish:
575 if (unset_environment)
576 unsetenv("NOTIFY_SOCKET");
577
8c47c732 578 return r;
8c47c732
LP
579}
580
a354329f
LP
581_public_ int sd_pid_notify(pid_t pid, int unset_environment, const char *state) {
582 return sd_pid_notify_with_fds(pid, unset_environment, state, NULL, 0);
583}
584
be8f4e9e 585_public_ int sd_notify(int unset_environment, const char *state) {
a354329f 586 return sd_pid_notify_with_fds(0, unset_environment, state, NULL, 0);
be8f4e9e
LP
587}
588
589_public_ int sd_pid_notifyf(pid_t pid, int unset_environment, const char *format, ...) {
590 _cleanup_free_ char *p = NULL;
591 int r;
592
593 if (format) {
594 va_list ap;
595
596 va_start(ap, format);
597 r = vasprintf(&p, format, ap);
598 va_end(ap);
599
600 if (r < 0 || !p)
601 return -ENOMEM;
602 }
603
604 return sd_pid_notify(pid, unset_environment, p);
605}
606
0ebee881 607_public_ int sd_notifyf(int unset_environment, const char *format, ...) {
be8f4e9e 608 _cleanup_free_ char *p = NULL;
8c47c732
LP
609 int r;
610
be8f4e9e
LP
611 if (format) {
612 va_list ap;
8c47c732 613
be8f4e9e
LP
614 va_start(ap, format);
615 r = vasprintf(&p, format, ap);
616 va_end(ap);
8c47c732 617
be8f4e9e
LP
618 if (r < 0 || !p)
619 return -ENOMEM;
620 }
8c47c732 621
be8f4e9e 622 return sd_pid_notify(0, unset_environment, p);
8c47c732 623}
40473a70 624
0ebee881 625_public_ int sd_booted(void) {
66e41181
LP
626 /* We test whether the runtime unit file directory has been
627 * created. This takes place in mount-setup.c, so is
628 * guaranteed to happen very early during boot. */
40473a70 629
31021ba0 630 return laccess("/run/systemd/system/", F_OK) >= 0;
40473a70 631}
09812eb7 632
0ebee881 633_public_ int sd_watchdog_enabled(int unset_environment, uint64_t *usec) {
a9becdd6 634 const char *s, *p = ""; /* p is set to dummy value to do unsetting */
be8f4e9e 635 uint64_t u;
a9becdd6 636 int r = 0;
09812eb7 637
a9becdd6
ZJS
638 s = getenv("WATCHDOG_USEC");
639 if (!s)
09812eb7 640 goto finish;
09812eb7 641
a9becdd6 642 r = safe_atou64(s, &u);
be8f4e9e 643 if (r < 0)
09812eb7 644 goto finish;
caffe412 645 if (u <= 0 || u >= USEC_INFINITY) {
09812eb7
LP
646 r = -EINVAL;
647 goto finish;
648 }
649
a9becdd6
ZJS
650 p = getenv("WATCHDOG_PID");
651 if (p) {
652 pid_t pid;
653
654 r = parse_pid(p, &pid);
655 if (r < 0)
656 goto finish;
657
658 /* Is this for us? */
df0ff127 659 if (getpid_cached() != pid) {
a9becdd6
ZJS
660 r = 0;
661 goto finish;
662 }
09812eb7
LP
663 }
664
665 if (usec)
be8f4e9e 666 *usec = u;
09812eb7
LP
667
668 r = 1;
669
670finish:
a9becdd6 671 if (unset_environment && s)
09812eb7 672 unsetenv("WATCHDOG_USEC");
a9becdd6
ZJS
673 if (unset_environment && p)
674 unsetenv("WATCHDOG_PID");
09812eb7
LP
675
676 return r;
09812eb7 677}