diff options
Diffstat (limited to 'lib')
39 files changed, 1078 insertions, 260 deletions
diff --git a/lib/agentx.c b/lib/agentx.c index 5f865ca2b8..821c573fb2 100644 --- a/lib/agentx.c +++ b/lib/agentx.c @@ -46,7 +46,7 @@ static struct list *events = NULL; static void agentx_events_update(void); -static int agentx_timeout(struct thread *t) +static void agentx_timeout(struct thread *t) { timeout_thr = NULL; @@ -54,10 +54,9 @@ static int agentx_timeout(struct thread *t) run_alarms(); netsnmp_check_outstanding_agent_requests(); agentx_events_update(); - return 0; } -static int agentx_read(struct thread *t) +static void agentx_read(struct thread *t) { fd_set fds; int flags, new_flags = 0; @@ -72,7 +71,7 @@ static int agentx_read(struct thread *t) if (-1 == flags) { flog_err(EC_LIB_SYSTEM_CALL, "Failed to get FD settings fcntl: %s(%d)", strerror(errno), errno); - return -1; + return; } if (flags & O_NONBLOCK) @@ -101,7 +100,6 @@ static int agentx_read(struct thread *t) netsnmp_check_outstanding_agent_requests(); agentx_events_update(); - return 0; } static void agentx_events_update(void) diff --git a/lib/base64.c b/lib/base64.c new file mode 100644 index 0000000000..e3f238969b --- /dev/null +++ b/lib/base64.c @@ -0,0 +1,193 @@ +/* + * This is part of the libb64 project, and has been placed in the public domain. + * For details, see http://sourceforge.net/projects/libb64 + */ + +#include "base64.h" + +static const int CHARS_PER_LINE = 72; +static const char *ENCODING = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +void base64_init_encodestate(struct base64_encodestate *state_in) +{ + state_in->step = step_A; + state_in->result = 0; + state_in->stepcount = 0; +} + +char base64_encode_value(char value_in) +{ + if (value_in > 63) + return '='; + return ENCODING[(int)value_in]; +} + +int base64_encode_block(const char *plaintext_in, int length_in, char *code_out, + struct base64_encodestate *state_in) +{ + const char *plainchar = plaintext_in; + const char *const plaintextend = plaintext_in + length_in; + char *codechar = code_out; + char result; + char fragment; + + result = state_in->result; + + switch (state_in->step) { + while (1) { + case step_A: + if (plainchar == plaintextend) { + state_in->result = result; + state_in->step = step_A; + return codechar - code_out; + } + fragment = *plainchar++; + result = (fragment & 0x0fc) >> 2; + *codechar++ = base64_encode_value(result); + result = (fragment & 0x003) << 4; + /* fall through */ + case step_B: + if (plainchar == plaintextend) { + state_in->result = result; + state_in->step = step_B; + return codechar - code_out; + } + fragment = *plainchar++; + result |= (fragment & 0x0f0) >> 4; + *codechar++ = base64_encode_value(result); + result = (fragment & 0x00f) << 2; + /* fall through */ + case step_C: + if (plainchar == plaintextend) { + state_in->result = result; + state_in->step = step_C; + return codechar - code_out; + } + fragment = *plainchar++; + result |= (fragment & 0x0c0) >> 6; + *codechar++ = base64_encode_value(result); + result = (fragment & 0x03f) >> 0; + *codechar++ = base64_encode_value(result); + + ++(state_in->stepcount); + if (state_in->stepcount == CHARS_PER_LINE/4) { + *codechar++ = '\n'; + state_in->stepcount = 0; + } + } + } + /* control should not reach here */ + return codechar - code_out; +} + +int base64_encode_blockend(char *code_out, struct base64_encodestate *state_in) +{ + char *codechar = code_out; + + switch (state_in->step) { + case step_B: + *codechar++ = base64_encode_value(state_in->result); + *codechar++ = '='; + *codechar++ = '='; + break; + case step_C: + *codechar++ = base64_encode_value(state_in->result); + *codechar++ = '='; + break; + case step_A: + break; + } + *codechar++ = '\n'; + + return codechar - code_out; +} + + +signed char base64_decode_value(signed char value_in) +{ + static const signed char decoding[] = { + 62, -1, -1, -1, 63, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, -1, + -1, -1, -2, -1, -1, -1, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, + -1, -1, -1, -1, -1, -1, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51 + }; + value_in -= 43; + if (value_in < 0 || value_in >= 80) + return -1; + return decoding[(int)value_in]; +} + +void base64_init_decodestate(struct base64_decodestate *state_in) +{ + state_in->step = step_a; + state_in->plainchar = 0; +} + +int base64_decode_block(const char *code_in, int length_in, char *plaintext_out, + struct base64_decodestate *state_in) +{ + const char *codec = code_in; + char *plainc = plaintext_out; + signed char fragmt; + + *plainc = state_in->plainchar; + + switch (state_in->step) { + while (1) { + case step_a: + do { + if (codec == code_in+length_in) { + state_in->step = step_a; + state_in->plainchar = *plainc; + return plainc - plaintext_out; + } + fragmt = base64_decode_value(*codec++); + } while (fragmt < 0); + *plainc = (fragmt & 0x03f) << 2; + /* fall through */ + case step_b: + do { + if (codec == code_in+length_in) { + state_in->step = step_b; + state_in->plainchar = *plainc; + return plainc - plaintext_out; + } + fragmt = base64_decode_value(*codec++); + } while (fragmt < 0); + *plainc++ |= (fragmt & 0x030) >> 4; + *plainc = (fragmt & 0x00f) << 4; + /* fall through */ + case step_c: + do { + if (codec == code_in+length_in) { + state_in->step = step_c; + state_in->plainchar = *plainc; + return plainc - plaintext_out; + } + fragmt = base64_decode_value(*codec++); + } while (fragmt < 0); + *plainc++ |= (fragmt & 0x03c) >> 2; + *plainc = (fragmt & 0x003) << 6; + /* fall through */ + case step_d: + do { + if (codec == code_in+length_in) { + state_in->step = step_d; + state_in->plainchar = *plainc; + return plainc - plaintext_out; + } + fragmt = base64_decode_value(*codec++); + } while (fragmt < 0); + *plainc++ |= (fragmt & 0x03f); + } + } + /* control should not reach here */ + return plainc - plaintext_out; +} diff --git a/lib/base64.h b/lib/base64.h new file mode 100644 index 0000000000..3dc1559aa4 --- /dev/null +++ b/lib/base64.h @@ -0,0 +1,45 @@ +/* + * This is part of the libb64 project, and has been placed in the public domain. + * For details, see http://sourceforge.net/projects/libb64 + */ + +#ifndef _BASE64_H_ +#define _BASE64_H_ + +enum base64_encodestep { + step_A, step_B, step_C +}; + +struct base64_encodestate { + enum base64_encodestep step; + char result; + int stepcount; +}; + +void base64_init_encodestate(struct base64_encodestate *state_in); + +char base64_encode_value(char value_in); + +int base64_encode_block(const char *plaintext_in, int length_in, char *code_out, + struct base64_encodestate *state_in); + +int base64_encode_blockend(char *code_out, struct base64_encodestate *state_in); + + +enum base64_decodestep { + step_a, step_b, step_c, step_d +}; + +struct base64_decodestate { + enum base64_decodestep step; + char plainchar; +}; + +void base64_init_decodestate(struct base64_decodestate *state_in); + +signed char base64_decode_value(signed char value_in); + +int base64_decode_block(const char *code_in, int length_in, char *plaintext_out, + struct base64_decodestate *state_in); + +#endif /* _BASE64_H_ */ @@ -461,14 +461,14 @@ static bool _bfd_sess_valid(const struct bfd_session_params *bsp) return true; } -static int _bfd_sess_send(struct thread *t) +static void _bfd_sess_send(struct thread *t) { struct bfd_session_params *bsp = THREAD_ARG(t); int rv; /* Validate configuration before trying to send bogus data. */ if (!_bfd_sess_valid(bsp)) - return 0; + return; if (bsp->lastev == BSE_INSTALL) { bsp->args.command = bsp->installed ? ZEBRA_BFD_DEST_UPDATE @@ -478,7 +478,7 @@ static int _bfd_sess_send(struct thread *t) /* If not installed and asked for uninstall, do nothing. */ if (!bsp->installed && bsp->args.command == ZEBRA_BFD_DEST_DEREGISTER) - return 0; + return; rv = zclient_bfd_command(bsglobal.zc, &bsp->args); /* Command was sent successfully. */ @@ -504,8 +504,6 @@ static int _bfd_sess_send(struct thread *t) bsp->lastev == BSE_INSTALL ? "installed" : "uninstalled"); } - - return 0; } static void _bfd_sess_remove(struct bfd_session_params *bsp) diff --git a/lib/checksum.c b/lib/checksum.c index 3473370041..6c5f06de45 100644 --- a/lib/checksum.c +++ b/lib/checksum.c @@ -9,13 +9,24 @@ #include <zebra.h> #include "checksum.h" -int /* return checksum in low-order 16 bits */ - in_cksum(void *parg, int nbytes) +#define add_carry(dst, add) \ + do { \ + typeof(dst) _add = (add); \ + dst += _add; \ + if (dst < _add) \ + dst++; \ + } while (0) + +uint16_t in_cksumv(const struct iovec *iov, size_t iov_len) { - unsigned short *ptr = parg; - register long sum; /* assumes long == 32 bits */ - unsigned short oddbyte; - register unsigned short answer; /* assumes unsigned short == 16 bits */ + const struct iovec *iov_end; + uint32_t sum = 0; + + union { + uint8_t bytes[2]; + uint16_t word; + } wordbuf; + bool have_oddbyte = false; /* * Our algorithm is simple, using a 32-bit accumulator (sum), @@ -23,17 +34,42 @@ int /* return checksum in low-order 16 bits */ * all the carry bits from the top 16 bits into the lower 16 bits. */ - sum = 0; - while (nbytes > 1) { - sum += *ptr++; - nbytes -= 2; + for (iov_end = iov + iov_len; iov < iov_end; iov++) { + const uint8_t *ptr, *end; + + ptr = (const uint8_t *)iov->iov_base; + end = ptr + iov->iov_len; + if (ptr == end) + continue; + + if (have_oddbyte) { + have_oddbyte = false; + wordbuf.bytes[1] = *ptr++; + + add_carry(sum, wordbuf.word); + } + + while (ptr + 8 <= end) { + add_carry(sum, *(const uint32_t *)(ptr + 0)); + add_carry(sum, *(const uint32_t *)(ptr + 4)); + ptr += 8; + } + + while (ptr + 2 <= end) { + add_carry(sum, *(const uint16_t *)ptr); + ptr += 2; + } + + if (ptr + 1 <= end) { + wordbuf.bytes[0] = *ptr++; + have_oddbyte = true; + } } /* mop up an odd byte, if necessary */ - if (nbytes == 1) { - oddbyte = 0; /* make sure top half is zero */ - *((uint8_t *)&oddbyte) = *(uint8_t *)ptr; /* one byte only */ - sum += oddbyte; + if (have_oddbyte) { + wordbuf.bytes[1] = 0; + add_carry(sum, wordbuf.word); } /* @@ -42,26 +78,7 @@ int /* return checksum in low-order 16 bits */ sum = (sum >> 16) + (sum & 0xffff); /* add high-16 to low-16 */ sum += (sum >> 16); /* add carry */ - answer = ~sum; /* ones-complement, then truncate to 16 bits */ - return (answer); -} - -int in_cksum_with_ph4(struct ipv4_ph *ph, void *data, int nbytes) -{ - uint8_t dat[sizeof(struct ipv4_ph) + nbytes]; - - memcpy(dat, ph, sizeof(struct ipv4_ph)); - memcpy(dat + sizeof(struct ipv4_ph), data, nbytes); - return in_cksum(dat, sizeof(dat)); -} - -int in_cksum_with_ph6(struct ipv6_ph *ph, void *data, int nbytes) -{ - uint8_t dat[sizeof(struct ipv6_ph) + nbytes]; - - memcpy(dat, ph, sizeof(struct ipv6_ph)); - memcpy(dat + sizeof(struct ipv6_ph), data, nbytes); - return in_cksum(dat, sizeof(dat)); + return ~sum; } /* Fletcher Checksum -- Refer to RFC1008. */ diff --git a/lib/checksum.h b/lib/checksum.h index 56771d4f24..508c3f38a6 100644 --- a/lib/checksum.h +++ b/lib/checksum.h @@ -1,3 +1,6 @@ +#ifndef _FRR_CHECKSUM_H +#define _FRR_CHECKSUM_H + #include <stdint.h> #include <netinet/in.h> @@ -24,9 +27,41 @@ struct ipv6_ph { uint8_t next_hdr; } __attribute__((packed)); -extern int in_cksum(void *data, int nbytes); -extern int in_cksum_with_ph4(struct ipv4_ph *ph, void *data, int nbytes); -extern int in_cksum_with_ph6(struct ipv6_ph *ph, void *data, int nbytes); + +extern uint16_t in_cksumv(const struct iovec *iov, size_t iov_len); + +static inline uint16_t in_cksum(const void *data, size_t nbytes) +{ + struct iovec iov[1]; + + iov[0].iov_base = (void *)data; + iov[0].iov_len = nbytes; + return in_cksumv(iov, array_size(iov)); +} + +static inline uint16_t in_cksum_with_ph4(const struct ipv4_ph *ph, + const void *data, size_t nbytes) +{ + struct iovec iov[2]; + + iov[0].iov_base = (void *)ph; + iov[0].iov_len = sizeof(*ph); + iov[1].iov_base = (void *)data; + iov[1].iov_len = nbytes; + return in_cksumv(iov, array_size(iov)); +} + +static inline uint16_t in_cksum_with_ph6(const struct ipv6_ph *ph, + const void *data, size_t nbytes) +{ + struct iovec iov[2]; + + iov[0].iov_base = (void *)ph; + iov[0].iov_len = sizeof(*ph); + iov[1].iov_base = (void *)data; + iov[1].iov_len = nbytes; + return in_cksumv(iov, array_size(iov)); +} #define FLETCHER_CHECKSUM_VALIDATE 0xffff extern uint16_t fletcher_checksum(uint8_t *, const size_t len, @@ -35,3 +70,5 @@ extern uint16_t fletcher_checksum(uint8_t *, const size_t len, #ifdef __cplusplus } #endif + +#endif /* _FRR_CHECKSUM_H */ diff --git a/lib/command.c b/lib/command.c index ebdbf162d1..1989668bf0 100644 --- a/lib/command.c +++ b/lib/command.c @@ -2239,9 +2239,9 @@ DEFUN (banner_motd_file, int cmd = cmd_banner_motd_file(filename); if (cmd == CMD_ERR_NO_FILE) - vty_out(vty, "%s does not exist", filename); + vty_out(vty, "%s does not exist\n", filename); else if (cmd == CMD_WARNING_CONFIG_FAILED) - vty_out(vty, "%s must be in %s", filename, SYSCONFDIR); + vty_out(vty, "%s must be in %s\n", filename, SYSCONFDIR); return cmd; } diff --git a/lib/frr_pthread.c b/lib/frr_pthread.c index 898fe98aad..0f56fbac83 100644 --- a/lib/frr_pthread.c +++ b/lib/frr_pthread.c @@ -237,18 +237,16 @@ void frr_pthread_stop_all(void) */ /* dummy task for sleeper pipe */ -static int fpt_dummy(struct thread *thread) +static void fpt_dummy(struct thread *thread) { - return 0; } /* poison pill task to end event loop */ -static int fpt_finish(struct thread *thread) +static void fpt_finish(struct thread *thread) { struct frr_pthread *fpt = THREAD_ARG(thread); atomic_store_explicit(&fpt->running, false, memory_order_relaxed); - return 0; } /* stop function, called from other threads to halt this one */ diff --git a/lib/frr_zmq.c b/lib/frr_zmq.c index e572558de1..db5c4c91a2 100644 --- a/lib/frr_zmq.c +++ b/lib/frr_zmq.c @@ -56,7 +56,7 @@ void frrzmq_finish(void) } } -static int frrzmq_read_msg(struct thread *t) +static void frrzmq_read_msg(struct thread *t) { struct frrzmq_cb **cbp = THREAD_ARG(t); struct frrzmq_cb *cb; @@ -67,10 +67,10 @@ static int frrzmq_read_msg(struct thread *t) size_t moresz; if (!cbp) - return 1; + return; cb = (*cbp); if (!cb || !cb->zmqsock) - return 1; + return; while (1) { zmq_pollitem_t polli = {.socket = cb->zmqsock, @@ -97,7 +97,7 @@ static int frrzmq_read_msg(struct thread *t) if (cb->write.cancelled && !cb->write.thread) XFREE(MTYPE_ZEROMQ_CB, *cbp); - return 0; + return; } continue; } @@ -129,7 +129,7 @@ static int frrzmq_read_msg(struct thread *t) if (cb->write.cancelled && !cb->write.thread) XFREE(MTYPE_ZEROMQ_CB, *cbp); - return 0; + return; } /* cb_part may have read additional parts of the @@ -153,14 +153,13 @@ static int frrzmq_read_msg(struct thread *t) thread_add_read(t->master, frrzmq_read_msg, cbp, cb->fd, &cb->read.thread); - return 0; + return; out_err: flog_err(EC_LIB_ZMQ, "ZeroMQ read error: %s(%d)", strerror(errno), errno); if (cb->read.cb_error) cb->read.cb_error(cb->read.arg, cb->zmqsock); - return 1; } int _frrzmq_thread_add_read(const struct xref_threadsched *xref, @@ -215,7 +214,7 @@ int _frrzmq_thread_add_read(const struct xref_threadsched *xref, return 0; } -static int frrzmq_write_msg(struct thread *t) +static void frrzmq_write_msg(struct thread *t) { struct frrzmq_cb **cbp = THREAD_ARG(t); struct frrzmq_cb *cb; @@ -223,10 +222,10 @@ static int frrzmq_write_msg(struct thread *t) int ret; if (!cbp) - return 1; + return; cb = (*cbp); if (!cb || !cb->zmqsock) - return 1; + return; while (1) { zmq_pollitem_t polli = {.socket = cb->zmqsock, @@ -252,7 +251,7 @@ static int frrzmq_write_msg(struct thread *t) if (cb->read.cancelled && !cb->read.thread) XFREE(MTYPE_ZEROMQ_CB, *cbp); - return 0; + return; } continue; } @@ -263,14 +262,13 @@ static int frrzmq_write_msg(struct thread *t) thread_add_write(t->master, frrzmq_write_msg, cbp, cb->fd, &cb->write.thread); - return 0; + return; out_err: flog_err(EC_LIB_ZMQ, "ZeroMQ write error: %s(%d)", strerror(errno), errno); if (cb->write.cb_error) cb->write.cb_error(cb->write.arg, cb->zmqsock); - return 1; } int _frrzmq_thread_add_write(const struct xref_threadsched *xref, @@ -490,7 +490,8 @@ struct connected *if_lookup_address(const void *matchaddr, int family, addr.family = AF_INET6; addr.u.prefix6 = *((struct in6_addr *)matchaddr); addr.prefixlen = IPV6_MAX_BITLEN; - } + } else + assert(!"Attempted lookup of family not supported"); match = NULL; diff --git a/lib/lib_errors.c b/lib/lib_errors.c index acc9a05c33..a658e4c295 100644 --- a/lib/lib_errors.c +++ b/lib/lib_errors.c @@ -69,6 +69,12 @@ static struct log_ref ferr_lib_warn[] = { .suggestion = "Gather log data and open an Issue", }, { + .code = EC_LIB_TIMER_TOO_LONG, + .title = "The Event subsystem has detected an internal timer that is scheduled to pop in greater than one year", + .description = "The Event subsystem has detected a timer being started that will pop in a timer that is greater than one year. This is a bug, please collect log data and open an issue.", + .suggestion = "Gather log data and open an Issue", + }, + { .code = EC_LIB_RMAP_RECURSION_LIMIT, .title = "Reached the Route-Map Recursion Limit", .description = "The Route-Map subsystem has detected a route-map depth of RMAP_RECURSION_LIMIT and has stopped processing", diff --git a/lib/lib_errors.h b/lib/lib_errors.h index 64ac6c1ceb..91f206f74a 100644 --- a/lib/lib_errors.h +++ b/lib/lib_errors.h @@ -48,6 +48,7 @@ enum lib_log_refs { EC_LIB_SLOW_THREAD_WALL, EC_LIB_STARVE_THREAD, EC_LIB_NO_THREAD, + EC_LIB_TIMER_TOO_LONG, EC_LIB_RMAP_RECURSION_LIMIT, EC_LIB_BACKUP_CONFIG, EC_LIB_VRF_LENGTH, diff --git a/lib/libfrr.c b/lib/libfrr.c index d5e326be41..10b3aad89e 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -962,7 +962,7 @@ static void frr_daemonize(void) * to read the config in after thread execution starts, so that * we can match this behavior. */ -static int frr_config_read_in(struct thread *t) +static void frr_config_read_in(struct thread *t) { hook_call(frr_config_pre, master); @@ -1000,8 +1000,6 @@ static int frr_config_read_in(struct thread *t) } hook_call(frr_config_post, master); - - return 0; } void frr_config_fork(void) @@ -1097,7 +1095,7 @@ static void frr_terminal_close(int isexit) static struct thread *daemon_ctl_thread = NULL; -static int frr_daemon_ctl(struct thread *t) +static void frr_daemon_ctl(struct thread *t) { char buf[1]; ssize_t nr; @@ -1106,7 +1104,7 @@ static int frr_daemon_ctl(struct thread *t) if (nr < 0 && (errno == EINTR || errno == EAGAIN)) goto out; if (nr <= 0) - return 0; + return; switch (buf[0]) { case 'S': /* SIGTSTP */ @@ -1131,7 +1129,6 @@ static int frr_daemon_ctl(struct thread *t) out: thread_add_read(master, frr_daemon_ctl, NULL, daemon_ctl_sock, &daemon_ctl_thread); - return 0; } void frr_detach(void) diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c index 70c71b18c4..1e25f6a1e2 100644 --- a/lib/northbound_cli.c +++ b/lib/northbound_cli.c @@ -347,7 +347,7 @@ int nb_cli_confirmed_commit_rollback(struct vty *vty) return ret; } -static int nb_cli_confirmed_commit_timeout(struct thread *thread) +static void nb_cli_confirmed_commit_timeout(struct thread *thread) { struct vty *vty = THREAD_ARG(thread); @@ -357,8 +357,6 @@ static int nb_cli_confirmed_commit_timeout(struct thread *thread) nb_cli_confirmed_commit_rollback(vty); nb_cli_confirmed_commit_clean(vty); - - return 0; } static int nb_cli_commit(struct vty *vty, bool force, diff --git a/lib/northbound_confd.c b/lib/northbound_confd.c index e1c8983fca..27eaefd071 100644 --- a/lib/northbound_confd.c +++ b/lib/northbound_confd.c @@ -283,7 +283,7 @@ frr_confd_cdb_diff_iter(confd_hkeypath_t *kp, enum cdb_iter_op cdb_op, return ITER_RECURSE; } -static int frr_confd_cdb_read_cb_prepare(int fd, int *subp, int reslen) +static void frr_confd_cdb_read_cb_prepare(int fd, int *subp, int reslen) { struct nb_context context = {}; struct nb_config *candidate; @@ -313,9 +313,9 @@ static int frr_confd_cdb_read_cb_prepare(int fd, int *subp, int reslen) 0, "Couldn't apply configuration changes") != CONFD_OK) { flog_err_confd("cdb_sub_abort_trans"); - return -1; + return; } - return 0; + return; } /* @@ -346,25 +346,23 @@ static int frr_confd_cdb_read_cb_prepare(int fd, int *subp, int reslen) errmsg) != CONFD_OK) { flog_err_confd("cdb_sub_abort_trans"); - return -1; + return; } } else { /* Acknowledge the notification. */ if (cdb_sync_subscription_socket(fd, CDB_DONE_PRIORITY) != CONFD_OK) { flog_err_confd("cdb_sync_subscription_socket"); - return -1; + return; } /* No configuration changes. */ if (!transaction) nb_config_free(candidate); } - - return 0; } -static int frr_confd_cdb_read_cb_commit(int fd, int *subp, int reslen) +static void frr_confd_cdb_read_cb_commit(int fd, int *subp, int reslen) { /* * No need to process the configuration changes again as we're already @@ -385,10 +383,8 @@ static int frr_confd_cdb_read_cb_commit(int fd, int *subp, int reslen) /* Acknowledge the notification. */ if (cdb_sync_subscription_socket(fd, CDB_DONE_PRIORITY) != CONFD_OK) { flog_err_confd("cdb_sync_subscription_socket"); - return -1; + return; } - - return 0; } static int frr_confd_cdb_read_cb_abort(int fd, int *subp, int reslen) @@ -417,7 +413,7 @@ static int frr_confd_cdb_read_cb_abort(int fd, int *subp, int reslen) return 0; } -static int frr_confd_cdb_read_cb(struct thread *thread) +static void frr_confd_cdb_read_cb(struct thread *thread) { int fd = THREAD_FD(thread); enum cdb_sub_notification cdb_ev; @@ -430,19 +426,22 @@ static int frr_confd_cdb_read_cb(struct thread *thread) if (cdb_read_subscription_socket2(fd, &cdb_ev, &flags, &subp, &reslen) != CONFD_OK) { flog_err_confd("cdb_read_subscription_socket2"); - return -1; + return; } switch (cdb_ev) { case CDB_SUB_PREPARE: - return frr_confd_cdb_read_cb_prepare(fd, subp, reslen); + frr_confd_cdb_read_cb_prepare(fd, subp, reslen); + break; case CDB_SUB_COMMIT: - return frr_confd_cdb_read_cb_commit(fd, subp, reslen); + frr_confd_cdb_read_cb_commit(fd, subp, reslen); + break; case CDB_SUB_ABORT: - return frr_confd_cdb_read_cb_abort(fd, subp, reslen); + frr_confd_cdb_read_cb_abort(fd, subp, reslen); + break; default: flog_err_confd("unknown CDB event"); - return -1; + break; } } @@ -1186,7 +1185,7 @@ static int frr_confd_dp_read(struct confd_daemon_ctx *dctx, int fd) return 0; } -static int frr_confd_dp_ctl_read(struct thread *thread) +static void frr_confd_dp_ctl_read(struct thread *thread) { struct confd_daemon_ctx *dctx = THREAD_ARG(thread); int fd = THREAD_FD(thread); @@ -1194,11 +1193,9 @@ static int frr_confd_dp_ctl_read(struct thread *thread) thread_add_read(master, frr_confd_dp_ctl_read, dctx, fd, &t_dp_ctl); frr_confd_dp_read(dctx, fd); - - return 0; } -static int frr_confd_dp_worker_read(struct thread *thread) +static void frr_confd_dp_worker_read(struct thread *thread) { struct confd_daemon_ctx *dctx = THREAD_ARG(thread); int fd = THREAD_FD(thread); @@ -1206,8 +1203,6 @@ static int frr_confd_dp_worker_read(struct thread *thread) thread_add_read(master, frr_confd_dp_worker_read, dctx, fd, &t_dp_worker); frr_confd_dp_read(dctx, fd); - - return 0; } static int frr_confd_subscribe_state(const struct lysc_node *snode, void *arg) diff --git a/lib/northbound_grpc.cpp b/lib/northbound_grpc.cpp index f5c2a91a50..e2a6290035 100644 --- a/lib/northbound_grpc.cpp +++ b/lib/northbound_grpc.cpp @@ -215,7 +215,7 @@ template <typename Q, typename S> class NewRpcState : RpcStateBase } - static int c_callback(struct thread *thread) + static void c_callback(struct thread *thread) { auto _tag = static_cast<NewRpcState<Q, S> *>(thread->arg); /* @@ -234,7 +234,7 @@ template <typename Q, typename S> class NewRpcState : RpcStateBase pthread_cond_signal(&_tag->cond); pthread_mutex_unlock(&_tag->cmux); - return 0; + return; } const char *name; @@ -1410,7 +1410,7 @@ static int frr_grpc_finish(void) * fork. This is done by scheduling this init function as an event task, since * the event loop doesn't run until after fork. */ -static int frr_grpc_module_very_late_init(struct thread *thread) +static void frr_grpc_module_very_late_init(struct thread *thread) { const char *args = THIS_MODULE->load_args; uint port = GRPC_DEFAULT_PORT; @@ -1428,11 +1428,10 @@ static int frr_grpc_module_very_late_init(struct thread *thread) if (frr_grpc_init(port) < 0) goto error; - return 0; + return; error: flog_err(EC_LIB_GRPC_INIT, "failed to initialize the gRPC module"); - return -1; } static int frr_grpc_module_late_init(struct thread_master *tm) diff --git a/lib/northbound_sysrepo.c b/lib/northbound_sysrepo.c index 86a159e507..0158d8ea0a 100644 --- a/lib/northbound_sysrepo.c +++ b/lib/northbound_sysrepo.c @@ -41,7 +41,7 @@ static sr_session_ctx_t *session; static sr_conn_ctx_t *connection; static struct nb_transaction *transaction; -static int frr_sr_read_cb(struct thread *thread); +static void frr_sr_read_cb(struct thread *thread); static int frr_sr_finish(void); /* Convert FRR YANG data value to sysrepo YANG data value. */ @@ -526,7 +526,7 @@ static int frr_sr_notification_send(const char *xpath, struct list *arguments) return NB_OK; } -static int frr_sr_read_cb(struct thread *thread) +static void frr_sr_read_cb(struct thread *thread) { struct yang_module *module = THREAD_ARG(thread); int fd = THREAD_FD(thread); @@ -536,12 +536,10 @@ static int frr_sr_read_cb(struct thread *thread) if (ret != SR_ERR_OK) { flog_err(EC_LIB_LIBSYSREPO, "%s: sr_fd_event_process(): %s", __func__, sr_strerror(ret)); - return -1; + return; } thread_add_read(master, frr_sr_read_cb, module, fd, &module->sr_thread); - - return 0; } static void frr_sr_subscribe_config(struct yang_module *module) diff --git a/lib/pullwr.c b/lib/pullwr.c index 15563d2471..5e836984b2 100644 --- a/lib/pullwr.c +++ b/lib/pullwr.c @@ -51,7 +51,7 @@ struct pullwr { DEFINE_MTYPE_STATIC(LIB, PULLWR_HEAD, "pull-driven write controller"); DEFINE_MTYPE_STATIC(LIB, PULLWR_BUF, "pull-driven write buffer"); -static int pullwr_run(struct thread *t); +static void pullwr_run(struct thread *t); struct pullwr *_pullwr_new(struct thread_master *tm, int fd, void *arg, @@ -189,7 +189,7 @@ void pullwr_write(struct pullwr *pullwr, const void *data, size_t len) pullwr_bump(pullwr); } -static int pullwr_run(struct thread *t) +static void pullwr_run(struct thread *t) { struct pullwr *pullwr = THREAD_ARG(t); struct iovec iov[2]; @@ -222,7 +222,7 @@ static int pullwr_run(struct thread *t) * into idle, i.e. no calling thread_add_write() */ pullwr_resize(pullwr, 0); - return 0; + return; } niov = pullwr_iov(pullwr, iov); @@ -233,12 +233,12 @@ static int pullwr_run(struct thread *t) if (errno == EAGAIN || errno == EWOULDBLOCK) break; pullwr->err(pullwr->arg, pullwr, false); - return 0; + return; } if (nwr == 0) { pullwr->err(pullwr->arg, pullwr, true); - return 0; + return; } pullwr->total_written += nwr; @@ -258,7 +258,6 @@ static int pullwr_run(struct thread *t) */ if (!maxspun) pullwr_resize(pullwr, 0); - return 0; } void pullwr_stats(struct pullwr *pullwr, uint64_t *total_written, diff --git a/lib/resolver.c b/lib/resolver.c index 29138bbc8d..93fa84bbe9 100644 --- a/lib/resolver.c +++ b/lib/resolver.c @@ -104,17 +104,15 @@ static void resolver_fd_drop_maybe(struct resolver_fd *resfd) static void resolver_update_timeouts(struct resolver_state *r); -static int resolver_cb_timeout(struct thread *t) +static void resolver_cb_timeout(struct thread *t) { struct resolver_state *r = THREAD_ARG(t); ares_process(r->channel, NULL, NULL); resolver_update_timeouts(r); - - return 0; } -static int resolver_cb_socket_readable(struct thread *t) +static void resolver_cb_socket_readable(struct thread *t) { struct resolver_fd *resfd = THREAD_ARG(t); struct resolver_state *r = resfd->state; @@ -127,11 +125,9 @@ static int resolver_cb_socket_readable(struct thread *t) */ ares_process_fd(r->channel, resfd->fd, ARES_SOCKET_BAD); resolver_update_timeouts(r); - - return 0; } -static int resolver_cb_socket_writable(struct thread *t) +static void resolver_cb_socket_writable(struct thread *t) { struct resolver_fd *resfd = THREAD_ARG(t); struct resolver_state *r = resfd->state; @@ -144,8 +140,6 @@ static int resolver_cb_socket_writable(struct thread *t) */ ares_process_fd(r->channel, ARES_SOCKET_BAD, resfd->fd); resolver_update_timeouts(r); - - return 0; } static void resolver_update_timeouts(struct resolver_state *r) @@ -232,7 +226,7 @@ static void ares_address_cb(void *arg, int status, int timeouts, callback(query, NULL, i, &addr[0]); } -static int resolver_cb_literal(struct thread *t) +static void resolver_cb_literal(struct thread *t) { struct resolver_query *query = THREAD_ARG(t); void (*callback)(struct resolver_query *, const char *, int, @@ -242,7 +236,6 @@ static int resolver_cb_literal(struct thread *t) query->callback = NULL; callback(query, ARES_SUCCESS, 1, &query->literal_addr); - return 0; } void resolver_resolve(struct resolver_query *query, int af, vrf_id_t vrf_id, diff --git a/lib/routemap.c b/lib/routemap.c index 7f733c8114..9afe18d10b 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -1799,12 +1799,11 @@ static struct list *route_map_get_index_list(struct route_node **rn, /* * This function returns the route-map index that best matches the prefix. */ -static struct route_map_index *route_map_get_index(struct route_map *map, - const struct prefix *prefix, - void *object, - uint8_t *match_ret) +static struct route_map_index * +route_map_get_index(struct route_map *map, const struct prefix *prefix, + void *object, enum route_map_cmd_result_t *match_ret) { - int ret = 0; + enum route_map_cmd_result_t ret = RMAP_NOMATCH; struct list *candidate_rmap_list = NULL; struct route_node *rn = NULL; struct listnode *ln = NULL, *nn = NULL; @@ -2559,7 +2558,7 @@ route_map_result_t route_map_apply_ext(struct route_map *map, if ((!map->optimization_disabled) && (map->ipv4_prefix_table || map->ipv6_prefix_table)) { index = route_map_get_index(map, prefix, match_object, - (uint8_t *)&match_ret); + &match_ret); if (index) { index->applied++; if (rmap_debug) diff --git a/lib/sigevent.c b/lib/sigevent.c index 00bc31f517..0f20bc0270 100644 --- a/lib/sigevent.c +++ b/lib/sigevent.c @@ -143,7 +143,7 @@ int frr_sigevent_process(void) #ifdef SIGEVENT_SCHEDULE_THREAD /* timer thread to check signals. shouldn't be needed */ -int frr_signal_timer(struct thread *t) +void frr_signal_timer(struct thread *t) { struct frr_sigevent_master_t *sigm; @@ -151,7 +151,7 @@ int frr_signal_timer(struct thread *t) sigm->t = NULL; thread_add_timer(sigm->t->master, frr_signal_timer, &sigmaster, FRR_SIGNAL_TIMER_INTERVAL, &sigm->t); - return frr_sigevent_process(); + frr_sigevent_process(); } #endif /* SIGEVENT_SCHEDULE_THREAD */ diff --git a/lib/spf_backoff.c b/lib/spf_backoff.c index a273e93463..117b7d3e88 100644 --- a/lib/spf_backoff.c +++ b/lib/spf_backoff.c @@ -117,17 +117,16 @@ void spf_backoff_free(struct spf_backoff *backoff) XFREE(MTYPE_SPF_BACKOFF, backoff); } -static int spf_backoff_timetolearn_elapsed(struct thread *thread) +static void spf_backoff_timetolearn_elapsed(struct thread *thread) { struct spf_backoff *backoff = THREAD_ARG(thread); backoff->state = SPF_BACKOFF_LONG_WAIT; backoff_debug("SPF Back-off(%s) TIMETOLEARN elapsed, move to state %s", backoff->name, spf_backoff_state2str(backoff->state)); - return 0; } -static int spf_backoff_holddown_elapsed(struct thread *thread) +static void spf_backoff_holddown_elapsed(struct thread *thread) { struct spf_backoff *backoff = THREAD_ARG(thread); @@ -136,7 +135,6 @@ static int spf_backoff_holddown_elapsed(struct thread *thread) backoff->state = SPF_BACKOFF_QUIET; backoff_debug("SPF Back-off(%s) HOLDDOWN elapsed, move to state %s", backoff->name, spf_backoff_state2str(backoff->state)); - return 0; } long spf_backoff_schedule(struct spf_backoff *backoff) diff --git a/lib/subdir.am b/lib/subdir.am index 648ab7f14a..b505e235ca 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -8,6 +8,7 @@ lib_libfrr_la_LIBADD = $(LIBCAP) $(UNWIND_LIBS) $(LIBYANG_LIBS) $(LUA_LIB) $(UST lib_libfrr_la_SOURCES = \ lib/agg_table.c \ lib/atomlist.c \ + lib/base64.c \ lib/bfd.c \ lib/buffer.c \ lib/checksum.c \ @@ -113,6 +114,7 @@ lib_libfrr_la_SOURCES = \ lib/zlog.c \ lib/zlog_5424.c \ lib/zlog_5424_cli.c \ + lib/zlog_live.c \ lib/zlog_targets.c \ lib/printf/printf-pos.c \ lib/printf/vfprintf.c \ @@ -177,6 +179,7 @@ clippy_scan += \ pkginclude_HEADERS += \ lib/agg_table.h \ lib/atomlist.h \ + lib/base64.h \ lib/bfd.h \ lib/bitfield.h \ lib/buffer.h \ @@ -287,6 +290,7 @@ pkginclude_HEADERS += \ lib/zebra.h \ lib/zlog.h \ lib/zlog_5424.h \ + lib/zlog_live.h \ lib/zlog_targets.h \ lib/pbr.h \ lib/routing_nb.h \ diff --git a/lib/systemd.c b/lib/systemd.c index 2238dc9f3d..1c9a6f122e 100644 --- a/lib/systemd.c +++ b/lib/systemd.c @@ -80,14 +80,13 @@ void systemd_send_stopping(void) static struct thread_master *systemd_master = NULL; -static int systemd_send_watchdog(struct thread *t) +static void systemd_send_watchdog(struct thread *t) { systemd_send_information("WATCHDOG=1"); assert(watchdog_msec > 0); thread_add_timer_msec(systemd_master, systemd_send_watchdog, NULL, watchdog_msec, NULL); - return 1; } void systemd_send_started(struct thread_master *m) diff --git a/lib/thread.c b/lib/thread.c index ada7a9cc80..90074b3d89 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -498,6 +498,41 @@ DEFUN (clear_thread_cpu, return CMD_SUCCESS; } +static void show_thread_timers_helper(struct vty *vty, struct thread_master *m) +{ + const char *name = m->name ? m->name : "main"; + char underline[strlen(name) + 1]; + struct thread *thread; + + memset(underline, '-', sizeof(underline)); + underline[sizeof(underline) - 1] = '\0'; + + vty_out(vty, "\nShowing timers for %s\n", name); + vty_out(vty, "-------------------%s\n", underline); + + frr_each (thread_timer_list, &m->timer, thread) { + vty_out(vty, " %-50s%pTH\n", thread->hist->funcname, thread); + } +} + +DEFPY_NOSH (show_thread_timers, + show_thread_timers_cmd, + "show thread timers", + SHOW_STR + "Thread information\n" + "Show all timers and how long they have in the system\n") +{ + struct listnode *node; + struct thread_master *m; + + frr_with_mutex (&masters_mtx) { + for (ALL_LIST_ELEMENTS_RO(masters, node, m)) + show_thread_timers_helper(vty, m); + } + + return CMD_SUCCESS; +} + void thread_cmd_init(void) { install_element(VIEW_NODE, &show_thread_cpu_cmd); @@ -509,6 +544,8 @@ void thread_cmd_init(void) install_element(CONFIG_NODE, &no_service_cputime_warning_cmd); install_element(CONFIG_NODE, &service_walltime_warning_cmd); install_element(CONFIG_NODE, &no_service_walltime_warning_cmd); + + install_element(VIEW_NODE, &show_thread_timers_cmd); } /* CLI end ------------------------------------------------------------------ */ @@ -773,7 +810,7 @@ char *thread_timer_to_hhmmss(char *buf, int buf_size, /* Get new thread. */ static struct thread *thread_get(struct thread_master *m, uint8_t type, - int (*func)(struct thread *), void *arg, + void (*func)(struct thread *), void *arg, const struct xref_threadsched *xref) { struct thread *thread = thread_list_pop(&m->unuse); @@ -930,7 +967,7 @@ done: /* Add new read thread. */ void _thread_add_read_write(const struct xref_threadsched *xref, struct thread_master *m, - int (*func)(struct thread *), void *arg, int fd, + void (*func)(struct thread *), void *arg, int fd, struct thread **t_ptr) { int dir = xref->thread_type; @@ -1010,7 +1047,7 @@ void _thread_add_read_write(const struct xref_threadsched *xref, static void _thread_add_timer_timeval(const struct xref_threadsched *xref, struct thread_master *m, - int (*func)(struct thread *), void *arg, + void (*func)(struct thread *), void *arg, struct timeval *time_relative, struct thread **t_ptr) { @@ -1052,12 +1089,18 @@ static void _thread_add_timer_timeval(const struct xref_threadsched *xref, if (thread_timer_list_first(&m->timer) == thread) AWAKEN(m); } +#define ONEYEAR2SEC (60 * 60 * 24 * 365) + if (time_relative->tv_sec > ONEYEAR2SEC) + flog_err( + EC_LIB_TIMER_TOO_LONG, + "Timer: %pTHD is created with an expiration that is greater than 1 year", + thread); } /* Add timer event thread. */ void _thread_add_timer(const struct xref_threadsched *xref, - struct thread_master *m, int (*func)(struct thread *), + struct thread_master *m, void (*func)(struct thread *), void *arg, long timer, struct thread **t_ptr) { struct timeval trel; @@ -1073,8 +1116,8 @@ void _thread_add_timer(const struct xref_threadsched *xref, /* Add timer event thread with "millisecond" resolution */ void _thread_add_timer_msec(const struct xref_threadsched *xref, struct thread_master *m, - int (*func)(struct thread *), void *arg, long timer, - struct thread **t_ptr) + void (*func)(struct thread *), void *arg, + long timer, struct thread **t_ptr) { struct timeval trel; @@ -1088,15 +1131,16 @@ void _thread_add_timer_msec(const struct xref_threadsched *xref, /* Add timer event thread with "timeval" resolution */ void _thread_add_timer_tv(const struct xref_threadsched *xref, - struct thread_master *m, int (*func)(struct thread *), - void *arg, struct timeval *tv, struct thread **t_ptr) + struct thread_master *m, + void (*func)(struct thread *), void *arg, + struct timeval *tv, struct thread **t_ptr) { _thread_add_timer_timeval(xref, m, func, arg, tv, t_ptr); } /* Add simple event thread. */ void _thread_add_event(const struct xref_threadsched *xref, - struct thread_master *m, int (*func)(struct thread *), + struct thread_master *m, void (*func)(struct thread *), void *arg, int val, struct thread **t_ptr) { struct thread *thread = NULL; @@ -1840,6 +1884,27 @@ unsigned long thread_consumed_time(RUSAGE_T *now, RUSAGE_T *start, unsigned long *cputime) { #ifdef HAVE_CLOCK_THREAD_CPUTIME_ID + +#ifdef __FreeBSD__ + /* + * FreeBSD appears to have an issue when calling clock_gettime + * with CLOCK_THREAD_CPUTIME_ID really close to each other + * occassionally the now time will be before the start time. + * This is not good and FRR is ending up with CPU HOG's + * when the subtraction wraps to very large numbers + * + * What we are going to do here is cheat a little bit + * and notice that this is a problem and just correct + * it so that it is impossible to happen + */ + if (start->cpu.tv_sec == now->cpu.tv_sec && + start->cpu.tv_nsec > now->cpu.tv_nsec) + now->cpu.tv_nsec = start->cpu.tv_nsec + 1; + else if (start->cpu.tv_sec > now->cpu.tv_sec) { + now->cpu.tv_sec = start->cpu.tv_sec; + now->cpu.tv_nsec = start->cpu.tv_nsec + 1; + } +#endif *cputime = (now->cpu.tv_sec - start->cpu.tv_sec) * TIMER_SECOND_MICRO + (now->cpu.tv_nsec - start->cpu.tv_nsec) / 1000; #else @@ -2008,7 +2073,7 @@ void thread_call(struct thread *thread) /* Execute thread */ void _thread_execute(const struct xref_threadsched *xref, - struct thread_master *m, int (*func)(struct thread *), + struct thread_master *m, void (*func)(struct thread *), void *arg, int val) { struct thread *thread; diff --git a/lib/thread.h b/lib/thread.h index 0c2a4ba869..a2049ae52a 100644 --- a/lib/thread.h +++ b/lib/thread.h @@ -114,7 +114,7 @@ struct thread { struct thread_timer_list_item timeritem; struct thread **ref; /* external reference (if given) */ struct thread_master *master; /* pointer to the struct thread_master */ - int (*func)(struct thread *); /* event function */ + void (*func)(struct thread *); /* event function */ void *arg; /* event argument */ union { int val; /* second argument of the event. */ @@ -134,7 +134,7 @@ struct thread { #endif struct cpu_thread_history { - int (*func)(struct thread *); + void (*func)(struct thread *); atomic_size_t total_cpu_warn; atomic_size_t total_wall_warn; atomic_size_t total_starv_warn; @@ -227,32 +227,32 @@ extern void thread_master_free_unused(struct thread_master *); extern void _thread_add_read_write(const struct xref_threadsched *xref, struct thread_master *master, - int (*fn)(struct thread *), void *arg, + void (*fn)(struct thread *), void *arg, int fd, struct thread **tref); extern void _thread_add_timer(const struct xref_threadsched *xref, struct thread_master *master, - int (*fn)(struct thread *), void *arg, long t, + void (*fn)(struct thread *), void *arg, long t, struct thread **tref); extern void _thread_add_timer_msec(const struct xref_threadsched *xref, struct thread_master *master, - int (*fn)(struct thread *), void *arg, + void (*fn)(struct thread *), void *arg, long t, struct thread **tref); extern void _thread_add_timer_tv(const struct xref_threadsched *xref, struct thread_master *master, - int (*fn)(struct thread *), void *arg, + void (*fn)(struct thread *), void *arg, struct timeval *tv, struct thread **tref); extern void _thread_add_event(const struct xref_threadsched *xref, struct thread_master *master, - int (*fn)(struct thread *), void *arg, int val, + void (*fn)(struct thread *), void *arg, int val, struct thread **tref); extern void _thread_execute(const struct xref_threadsched *xref, struct thread_master *master, - int (*fn)(struct thread *), void *arg, int val); + void (*fn)(struct thread *), void *arg, int val); extern void thread_cancel(struct thread **event); extern void thread_cancel_async(struct thread_master *, struct thread **, @@ -65,7 +65,7 @@ DEFINE_MTYPE_STATIC(LIB, VTY_HIST, "VTY history"); DECLARE_DLIST(vtys, struct vty, itm); /* Vty events */ -enum event { +enum vty_event { VTY_SERV, VTY_READ, VTY_WRITE, @@ -90,8 +90,8 @@ struct vty_serv { DECLARE_DLIST(vtyservs, struct vty_serv, itm); -static void vty_event_serv(enum event event, struct vty_serv *); -static void vty_event(enum event, struct vty *); +static void vty_event_serv(enum vty_event event, struct vty_serv *); +static void vty_event(enum vty_event, struct vty *); /* Extern host structure from command.c */ extern struct host host; @@ -1299,7 +1299,7 @@ static void vty_buffer_reset(struct vty *vty) } /* Read data via vty socket. */ -static int vty_read(struct thread *thread) +static void vty_read(struct thread *thread) { int i; int nbytes; @@ -1312,10 +1312,8 @@ static int vty_read(struct thread *thread) if (nbytes < 0) { if (ERRNO_IO_RETRY(errno)) { vty_event(VTY_READ, vty); - return 0; + return; } - vty->monitor = 0; /* disable monitoring to avoid - infinite recursion */ flog_err( EC_LIB_SOCKET, "%s: read error on vty client fd %d, closing: %s", @@ -1464,6 +1462,11 @@ static int vty_read(struct thread *thread) vty_out(vty, "\n"); buffer_flush_available(vty->obuf, vty->wfd); vty_execute(vty); + + if (vty->pass_fd != -1) { + close(vty->pass_fd); + vty->pass_fd = -1; + } break; case '\t': vty_complete_command(vty); @@ -1496,11 +1499,10 @@ static int vty_read(struct thread *thread) vty_event(VTY_WRITE, vty); vty_event(VTY_READ, vty); } - return 0; } /* Flush buffer to the vty. */ -static int vty_flush(struct thread *thread) +static void vty_flush(struct thread *thread) { int erase; buffer_status_t flushrc; @@ -1525,14 +1527,12 @@ static int vty_flush(struct thread *thread) vty->lines >= 0 ? vty->lines : vty->height, erase, 0); switch (flushrc) { case BUFFER_ERROR: - vty->monitor = - 0; /* disable monitoring to avoid infinite recursion */ zlog_info("buffer_flush failed on vty client fd %d/%d, closing", vty->fd, vty->wfd); buffer_reset(vty->lbuf); buffer_reset(vty->obuf); vty_close(vty); - return 0; + return; case BUFFER_EMPTY: if (vty->status == VTY_CLOSE) vty_close(vty); @@ -1549,8 +1549,6 @@ static int vty_flush(struct thread *thread) vty_event(VTY_WRITE, vty); break; } - - return 0; } /* Allocate new vty struct. */ @@ -1564,6 +1562,7 @@ struct vty *vty_new(void) new->obuf = buffer_new(0); /* Use default buffer size. */ new->buf = XCALLOC(MTYPE_VTY, VTY_BUFSIZ); new->max = VTY_BUFSIZ; + new->pass_fd = -1; return new; } @@ -1753,7 +1752,7 @@ struct vty *vty_stdio(void (*atclose)(int isexit)) } /* Accept connection from the network. */ -static int vty_accept(struct thread *thread) +static void vty_accept(struct thread *thread) { struct vty_serv *vtyserv = THREAD_ARG(thread); int vty_sock; @@ -1774,7 +1773,7 @@ static int vty_accept(struct thread *thread) if (vty_sock < 0) { flog_err(EC_LIB_SOCKET, "can't accept vty socket : %s", safe_strerror(errno)); - return -1; + return; } set_nonblocking(vty_sock); set_cloexec(vty_sock); @@ -1783,7 +1782,7 @@ static int vty_accept(struct thread *thread) close(vty_sock); zlog_info("Vty unable to convert prefix from sockunion %pSU", &su); - return -1; + return; } /* VTY's accesslist apply. */ @@ -1792,7 +1791,7 @@ static int vty_accept(struct thread *thread) && (access_list_apply(acl, &p) == FILTER_DENY)) { zlog_info("Vty connection refused from %pSU", &su); close(vty_sock); - return 0; + return; } } @@ -1803,7 +1802,7 @@ static int vty_accept(struct thread *thread) && (access_list_apply(acl, &p) == FILTER_DENY)) { zlog_info("Vty connection refused from %pSU", &su); close(vty_sock); - return 0; + return; } } @@ -1817,8 +1816,6 @@ static int vty_accept(struct thread *thread) zlog_info("Vty connection from %pSU", &su); vty_create(vty_sock, &su); - - return 0; } static void vty_serv_sock_addrinfo(const char *hostname, unsigned short port) @@ -1968,7 +1965,7 @@ static void vty_serv_un(const char *path) /* #define VTYSH_DEBUG 1 */ -static int vtysh_accept(struct thread *thread) +static void vtysh_accept(struct thread *thread) { struct vty_serv *vtyserv = THREAD_ARG(thread); int accept_sock = vtyserv->sock; @@ -1988,7 +1985,7 @@ static int vtysh_accept(struct thread *thread) if (sock < 0) { flog_err(EC_LIB_SOCKET, "can't accept vty socket : %s", safe_strerror(errno)); - return -1; + return; } if (set_nonblocking(sock) < 0) { @@ -1997,7 +1994,7 @@ static int vtysh_accept(struct thread *thread) "vtysh_accept: could not set vty socket %d to non-blocking, %s, closing", sock, safe_strerror(errno)); close(sock); - return -1; + return; } set_cloexec(sock); @@ -2013,19 +2010,70 @@ static int vtysh_accept(struct thread *thread) vtys_add_tail(vtysh_sessions, vty); vty_event(VTYSH_READ, vty); +} - return 0; +static int vtysh_do_pass_fd(struct vty *vty) +{ + struct iovec iov[1] = { + { + .iov_base = vty->pass_fd_status, + .iov_len = sizeof(vty->pass_fd_status), + }, + }; + union { + uint8_t buf[CMSG_SPACE(sizeof(int))]; + struct cmsghdr align; + } u; + struct msghdr mh = { + .msg_iov = iov, + .msg_iovlen = array_size(iov), + .msg_control = u.buf, + .msg_controllen = sizeof(u.buf), + }; + struct cmsghdr *cmh = CMSG_FIRSTHDR(&mh); + ssize_t ret; + + cmh->cmsg_level = SOL_SOCKET; + cmh->cmsg_type = SCM_RIGHTS; + cmh->cmsg_len = CMSG_LEN(sizeof(int)); + memcpy(CMSG_DATA(cmh), &vty->pass_fd, sizeof(int)); + + ret = sendmsg(vty->wfd, &mh, 0); + if (ret < 0 && ERRNO_IO_RETRY(errno)) + return BUFFER_PENDING; + + close(vty->pass_fd); + vty->pass_fd = -1; + vty->status = VTY_NORMAL; + + if (ret <= 0) + return BUFFER_ERROR; + + /* resume accepting commands (suspended in vtysh_read) */ + vty_event(VTYSH_READ, vty); + + if ((size_t)ret < sizeof(vty->pass_fd_status)) { + size_t remains = sizeof(vty->pass_fd_status) - ret; + + buffer_put(vty->obuf, vty->pass_fd_status + ret, remains); + return BUFFER_PENDING; + } + return BUFFER_EMPTY; } static int vtysh_flush(struct vty *vty) { - switch (buffer_flush_available(vty->obuf, vty->wfd)) { + int ret; + + ret = buffer_flush_available(vty->obuf, vty->wfd); + if (ret == BUFFER_EMPTY && vty->status == VTY_PASSFD) + ret = vtysh_do_pass_fd(vty); + + switch (ret) { case BUFFER_PENDING: vty_event(VTYSH_WRITE, vty); break; case BUFFER_ERROR: - vty->monitor = - 0; /* disable monitoring to avoid infinite recursion */ flog_err(EC_LIB_SOCKET, "%s: write error to fd %d, closing", __func__, vty->fd); buffer_reset(vty->lbuf); @@ -2038,7 +2086,15 @@ static int vtysh_flush(struct vty *vty) return 0; } -static int vtysh_read(struct thread *thread) +void vty_pass_fd(struct vty *vty, int fd) +{ + if (vty->pass_fd != -1) + close(vty->pass_fd); + + vty->pass_fd = fd; +} + +static void vtysh_read(struct thread *thread) { int ret; int sock; @@ -2055,10 +2111,8 @@ static int vtysh_read(struct thread *thread) if (nbytes < 0) { if (ERRNO_IO_RETRY(errno)) { vty_event(VTYSH_READ, vty); - return 0; + return; } - vty->monitor = 0; /* disable monitoring to avoid - infinite recursion */ flog_err( EC_LIB_SOCKET, "%s: read failed on vtysh client fd %d, closing: %s", @@ -2070,7 +2124,7 @@ static int vtysh_read(struct thread *thread) #ifdef VTYSH_DEBUG printf("close vtysh\n"); #endif /* VTYSH_DEBUG */ - return 0; + return; } #ifdef VTYSH_DEBUG @@ -2097,6 +2151,26 @@ static int vtysh_read(struct thread *thread) printf("vtysh node: %d\n", vty->node); #endif /* VTYSH_DEBUG */ + if (vty->pass_fd != -1) { + memset(vty->pass_fd_status, 0, 4); + vty->pass_fd_status[3] = ret; + vty->status = VTY_PASSFD; + + if (!vty->t_write) + vty_event(VTYSH_WRITE, vty); + + /* this introduces a "sequence point" + * command output is written normally, + * read processing is suspended until + * buffer is empty + * then retcode + FD is written + * then normal processing resumes + * + * => skip vty_event(VTYSH_READ, vty)! + */ + return; + } + /* hack for asynchronous "write integrated" * - other commands in "buf" will be ditched * - input during pending config-write is @@ -2112,7 +2186,7 @@ static int vtysh_read(struct thread *thread) if (!vty->t_write && (vtysh_flush(vty) < 0)) /* Try to flush results; exit if a write * error occurs. */ - return 0; + return; } } } @@ -2121,16 +2195,13 @@ static int vtysh_read(struct thread *thread) vty_close(vty); else vty_event(VTYSH_READ, vty); - - return 0; } -static int vtysh_write(struct thread *thread) +static void vtysh_write(struct thread *thread) { struct vty *vty = THREAD_ARG(thread); vtysh_flush(vty); - return 0; } #endif /* VTYSH */ @@ -2171,6 +2242,12 @@ void vty_close(struct vty *vty) THREAD_OFF(vty->t_write); THREAD_OFF(vty->t_timeout); + if (vty->pass_fd != -1) { + close(vty->pass_fd); + vty->pass_fd = -1; + } + zlog_live_close(&vty->live_log); + /* Flush buffer. */ buffer_flush_all(vty->obuf, vty->wfd); @@ -2221,7 +2298,7 @@ void vty_close(struct vty *vty) } /* When time out occur output message then close connection. */ -static int vty_timeout(struct thread *thread) +static void vty_timeout(struct thread *thread) { struct vty *vty; @@ -2236,8 +2313,6 @@ static int vty_timeout(struct thread *thread) /* Close connection. */ vty->status = VTY_CLOSE; vty_close(vty); - - return 0; } /* Read up configuration file from file_name. */ @@ -2608,7 +2683,7 @@ int vty_config_node_exit(struct vty *vty) /* Master of the threads. */ static struct thread_master *vty_master; -static void vty_event_serv(enum event event, struct vty_serv *vty_serv) +static void vty_event_serv(enum vty_event event, struct vty_serv *vty_serv) { switch (event) { case VTY_SERV: @@ -2626,7 +2701,7 @@ static void vty_event_serv(enum event event, struct vty_serv *vty_serv) } } -static void vty_event(enum event event, struct vty *vty) +static void vty_event(enum vty_event event, struct vty *vty) { switch (event) { #ifdef VTYSH @@ -2673,8 +2748,9 @@ DEFUN_NOSH (config_who, struct vty *v; frr_each (vtys, vty_sessions, v) - vty_out(vty, "%svty[%d] connected from %s.\n", - v->config ? "*" : " ", v->fd, v->address); + vty_out(vty, "%svty[%d] connected from %s%s.\n", + v->config ? "*" : " ", v->fd, v->address, + zlog_live_is_null(&v->live_log) ? "" : ", live log"); return CMD_SUCCESS; } @@ -2867,35 +2943,56 @@ DEFUN (no_service_advanced_vty, return CMD_SUCCESS; } -DEFUN_NOSH (terminal_monitor, - terminal_monitor_cmd, - "terminal monitor", - "Set terminal line parameters\n" - "Copy debug output to the current terminal line\n") +DEFUN_NOSH(terminal_monitor, + terminal_monitor_cmd, + "terminal monitor [detach]", + "Set terminal line parameters\n" + "Copy debug output to the current terminal line\n" + "Keep logging feed open independent of VTY session\n") { - vty->monitor = 1; + int fd_ret = -1; + + if (vty->type != VTY_SHELL_SERV) { + vty_out(vty, "%% not supported\n"); + return CMD_WARNING; + } + + if (argc == 3) { + struct zlog_live_cfg detach_log = {}; + + zlog_live_open(&detach_log, LOG_DEBUG, &fd_ret); + zlog_live_disown(&detach_log); + } else + zlog_live_open(&vty->live_log, LOG_DEBUG, &fd_ret); + + if (fd_ret == -1) { + vty_out(vty, "%% error opening live log: %m\n"); + return CMD_WARNING; + } + + vty_pass_fd(vty, fd_ret); return CMD_SUCCESS; } -DEFUN_NOSH (terminal_no_monitor, - terminal_no_monitor_cmd, - "terminal no monitor", - "Set terminal line parameters\n" - NO_STR - "Copy debug output to the current terminal line\n") +DEFUN_NOSH(no_terminal_monitor, + no_terminal_monitor_cmd, + "no terminal monitor", + NO_STR + "Set terminal line parameters\n" + "Copy debug output to the current terminal line\n") { - vty->monitor = 0; + zlog_live_close(&vty->live_log); return CMD_SUCCESS; } -DEFUN_NOSH (no_terminal_monitor, - no_terminal_monitor_cmd, - "no terminal monitor", - NO_STR - "Set terminal line parameters\n" - "Copy debug output to the current terminal line\n") +DEFUN_NOSH(terminal_no_monitor, + terminal_no_monitor_cmd, + "terminal no monitor", + "Set terminal line parameters\n" + NO_STR + "Copy debug output to the current terminal line\n") { - return terminal_no_monitor(self, vty, argc, argv); + return no_terminal_monitor(self, vty, argc, argv); } @@ -34,6 +34,7 @@ #include "qobj.h" #include "compiler.h" #include "northbound.h" +#include "zlog_live.h" #ifdef __cplusplus extern "C" { @@ -161,7 +162,22 @@ struct vty { unsigned char escape; /* Current vty status. */ - enum { VTY_NORMAL, VTY_CLOSE, VTY_MORE, VTY_MORELINE } status; + enum { + VTY_NORMAL, + VTY_CLOSE, + VTY_MORE, + VTY_MORELINE, + VTY_PASSFD, + } status; + + /* vtysh socket/fd passing (for terminal monitor) */ + int pass_fd; + + /* CLI command return value (likely CMD_SUCCESS) when pass_fd != -1 */ + uint8_t pass_fd_status[4]; + + /* live logging target / terminal monitor */ + struct zlog_live_cfg live_log; /* IAC handling: was the last character received the IAC (interpret-as-command) escape character (and therefore the next @@ -186,9 +202,6 @@ struct vty { /* Configure lines. */ int lines; - /* Terminal monitor. */ - int monitor; - /* Read and write thread. */ struct thread *t_read; struct thread *t_write; @@ -329,6 +342,11 @@ extern bool vty_set_include(struct vty *vty, const char *regexp); */ extern int vty_json(struct vty *vty, struct json_object *json); +/* post fd to be passed to the vtysh client + * fd is owned by the VTY code after this and will be closed when done + */ +extern void vty_pass_fd(struct vty *vty, int fd); + extern bool vty_read_config(struct nb_config *config, const char *config_file, char *config_default_dir); extern void vty_time_print(struct vty *, int); diff --git a/lib/wheel.c b/lib/wheel.c index 1a0469b256..463410bea4 100644 --- a/lib/wheel.c +++ b/lib/wheel.c @@ -29,9 +29,9 @@ DEFINE_MTYPE_STATIC(LIB, TIMER_WHEEL_LIST, "Timer Wheel Slot List"); static int debug_timer_wheel = 0; -static int wheel_timer_thread(struct thread *t); +static void wheel_timer_thread(struct thread *t); -static int wheel_timer_thread_helper(struct thread *t) +static void wheel_timer_thread_helper(struct thread *t) { struct listnode *node, *nextnode; unsigned long long curr_slot; @@ -63,19 +63,15 @@ static int wheel_timer_thread_helper(struct thread *t) wheel->slots_to_skip = slots_to_skip; thread_add_timer_msec(wheel->master, wheel_timer_thread, wheel, wheel->nexttime * slots_to_skip, &wheel->timer); - - return 0; } -static int wheel_timer_thread(struct thread *t) +static void wheel_timer_thread(struct thread *t) { struct timer_wheel *wheel; wheel = THREAD_ARG(t); thread_execute(wheel->master, wheel_timer_thread_helper, wheel, 0); - - return 0; } struct timer_wheel *wheel_init(struct thread_master *master, int period, diff --git a/lib/workqueue.c b/lib/workqueue.c index 86afe4082e..92869594dd 100644 --- a/lib/workqueue.c +++ b/lib/workqueue.c @@ -238,7 +238,7 @@ void work_queue_unplug(struct work_queue *wq) * will reschedule itself if required, * otherwise work_queue_item_add */ -int work_queue_run(struct thread *thread) +void work_queue_run(struct thread *thread) { struct work_queue *wq; struct work_queue_item *item, *titem; @@ -388,6 +388,4 @@ stats: } else if (wq->spec.completion_func) wq->spec.completion_func(wq); - - return 0; } diff --git a/lib/workqueue.h b/lib/workqueue.h index b076ed0d28..39202dcda7 100644 --- a/lib/workqueue.h +++ b/lib/workqueue.h @@ -177,7 +177,7 @@ extern void work_queue_unplug(struct work_queue *wq); bool work_queue_is_scheduled(struct work_queue *); /* Helpers, exported for thread.c and command.c */ -extern int work_queue_run(struct thread *); +extern void work_queue_run(struct thread *); extern void workqueue_cmd_init(void); diff --git a/lib/yang_wrappers.c b/lib/yang_wrappers.c index 85aa003db7..bee76c6e0f 100644 --- a/lib/yang_wrappers.c +++ b/lib/yang_wrappers.c @@ -19,6 +19,7 @@ #include <zebra.h> +#include "base64.h" #include "log.h" #include "lib_errors.h" #include "northbound.h" @@ -677,6 +678,64 @@ void yang_get_default_string_buf(char *buf, size_t size, const char *xpath_fmt, } /* + * Primitive type: binary. + */ +struct yang_data *yang_data_new_binary(const char *xpath, const char *value, + size_t len) +{ + char *value_str; + struct base64_encodestate s; + int cnt; + char *c; + struct yang_data *data; + + value_str = (char *)malloc(len * 2); + base64_init_encodestate(&s); + cnt = base64_encode_block(value, len, value_str, &s); + c = value_str + cnt; + cnt = base64_encode_blockend(c, &s); + c += cnt; + *c = 0; + data = yang_data_new(xpath, value_str); + free(value_str); + return data; +} + +size_t yang_dnode_get_binary_buf(char *buf, size_t size, + const struct lyd_node *dnode, + const char *xpath_fmt, ...) +{ + const char *canon; + size_t cannon_len; + size_t decode_len; + size_t ret_len; + size_t cnt; + char *value_str; + struct base64_decodestate s; + + canon = YANG_DNODE_XPATH_GET_CANON(dnode, xpath_fmt); + cannon_len = strlen(canon); + decode_len = cannon_len; + value_str = (char *)malloc(decode_len); + base64_init_decodestate(&s); + cnt = base64_decode_block(canon, cannon_len, value_str, &s); + + ret_len = size > cnt ? cnt : size; + memcpy(buf, value_str, ret_len); + if (size < cnt) { + char xpath[XPATH_MAXLEN]; + + yang_dnode_get_path(dnode, xpath, sizeof(xpath)); + flog_warn(EC_LIB_YANG_DATA_TRUNCATED, + "%s: value was truncated [xpath %s]", __func__, + xpath); + } + free(value_str); + return ret_len; +} + + +/* * Primitive type: empty. */ struct yang_data *yang_data_new_empty(const char *xpath) diff --git a/lib/yang_wrappers.h b/lib/yang_wrappers.h index d781dfb1e4..56b314876f 100644 --- a/lib/yang_wrappers.h +++ b/lib/yang_wrappers.h @@ -118,6 +118,13 @@ extern const char *yang_get_default_string(const char *xpath_fmt, ...); extern void yang_get_default_string_buf(char *buf, size_t size, const char *xpath_fmt, ...); +/* binary */ +extern struct yang_data *yang_data_new_binary(const char *xpath, + const char *value, size_t len); +extern size_t yang_dnode_get_binary_buf(char *buf, size_t size, + const struct lyd_node *dnode, + const char *xpath_fmt, ...); + /* empty */ extern struct yang_data *yang_data_new_empty(const char *xpath); extern bool yang_dnode_get_empty(const struct lyd_node *dnode, diff --git a/lib/zclient.c b/lib/zclient.c index ab2dd09896..930adf6a7a 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -47,10 +47,10 @@ DEFINE_MTYPE_STATIC(LIB, ZCLIENT, "Zclient"); DEFINE_MTYPE_STATIC(LIB, REDIST_INST, "Redistribution instance IDs"); /* Zebra client events. */ -enum event { ZCLIENT_SCHEDULE, ZCLIENT_READ, ZCLIENT_CONNECT }; +enum zclient_event { ZCLIENT_SCHEDULE, ZCLIENT_READ, ZCLIENT_CONNECT }; /* Prototype for event manager. */ -static void zclient_event(enum event, struct zclient *); +static void zclient_event(enum zclient_event, struct zclient *); static void zebra_interface_if_set_value(struct stream *s, struct interface *ifp); @@ -263,20 +263,21 @@ static enum zclient_send_status zclient_failed(struct zclient *zclient) return ZCLIENT_SEND_FAILURE; } -static int zclient_flush_data(struct thread *thread) +static void zclient_flush_data(struct thread *thread) { struct zclient *zclient = THREAD_ARG(thread); zclient->t_write = NULL; if (zclient->sock < 0) - return -1; + return; switch (buffer_flush_available(zclient->wb, zclient->sock)) { case BUFFER_ERROR: flog_err( EC_LIB_ZAPI_SOCKET, "%s: buffer_flush_available failed on zclient fd %d, closing", __func__, zclient->sock); - return zclient_failed(zclient); + zclient_failed(zclient); + return; case BUFFER_PENDING: zclient->t_write = NULL; thread_add_write(zclient->master, zclient_flush_data, zclient, @@ -287,7 +288,6 @@ static int zclient_flush_data(struct thread *thread) (*zclient->zebra_buffer_write_ready)(); break; } - return 0; } /* @@ -754,7 +754,7 @@ void zclient_init(struct zclient *zclient, int redist_default, /* This function is a wrapper function for calling zclient_start from timer or event thread. */ -static int zclient_connect(struct thread *t) +static void zclient_connect(struct thread *t) { struct zclient *zclient; @@ -764,7 +764,7 @@ static int zclient_connect(struct thread *t) if (zclient_debug) zlog_debug("zclient_connect is called"); - return zclient_start(zclient); + zclient_start(zclient); } enum zclient_send_status zclient_send_rnh(struct zclient *zclient, int command, @@ -3864,7 +3864,7 @@ static zclient_handler *const lib_handlers[] = { }; /* Zebra client message read function. */ -static int zclient_read(struct thread *thread) +static void zclient_read(struct thread *thread) { size_t already; uint16_t length, command; @@ -3888,11 +3888,12 @@ static int zclient_read(struct thread *thread) zlog_debug( "zclient connection closed socket [%d].", zclient->sock); - return zclient_failed(zclient); + zclient_failed(zclient); + return; } if (nbyte != (ssize_t)(ZEBRA_HEADER_SIZE - already)) { zclient_event(ZCLIENT_READ, zclient); - return 0; + return; } already = ZEBRA_HEADER_SIZE; } @@ -3912,14 +3913,16 @@ static int zclient_read(struct thread *thread) EC_LIB_ZAPI_MISSMATCH, "%s: socket %d version mismatch, marker %d, version %d", __func__, zclient->sock, marker, version); - return zclient_failed(zclient); + zclient_failed(zclient); + return; } if (length < ZEBRA_HEADER_SIZE) { flog_err(EC_LIB_ZAPI_MISSMATCH, "%s: socket %d message length %u is less than %d ", __func__, zclient->sock, length, ZEBRA_HEADER_SIZE); - return zclient_failed(zclient); + zclient_failed(zclient); + return; } /* Length check. */ @@ -3947,12 +3950,13 @@ static int zclient_read(struct thread *thread) zlog_debug( "zclient connection closed socket [%d].", zclient->sock); - return zclient_failed(zclient); + zclient_failed(zclient); + return; } if (nbyte != (ssize_t)(length - already)) { /* Try again later. */ zclient_event(ZCLIENT_READ, zclient); - return 0; + return; } } @@ -3969,13 +3973,11 @@ static int zclient_read(struct thread *thread) if (zclient->sock < 0) /* Connection was closed during packet processing. */ - return -1; + return; /* Register read thread. */ stream_reset(zclient->ibuf); zclient_event(ZCLIENT_READ, zclient); - - return 0; } void zclient_redistribute(int command, struct zclient *zclient, afi_t afi, @@ -4036,7 +4038,7 @@ void zclient_redistribute_default(int command, struct zclient *zclient, zebra_redistribute_default_send(command, zclient, afi, vrf_id); } -static void zclient_event(enum event event, struct zclient *zclient) +static void zclient_event(enum zclient_event event, struct zclient *zclient) { switch (event) { case ZCLIENT_SCHEDULE: diff --git a/lib/zlog.c b/lib/zlog.c index 1b0751559d..85606d2624 100644 --- a/lib/zlog.c +++ b/lib/zlog.c @@ -908,6 +908,11 @@ size_t zlog_msg_ts_3164(struct zlog_msg *msg, struct fbuf *out, uint32_t flags) return bputs(out, msg->ts_3164_str); } +void zlog_msg_tsraw(struct zlog_msg *msg, struct timespec *ts) +{ + memcpy(ts, &msg->ts, sizeof(*ts)); +} + void zlog_set_prefix_ec(bool enable) { atomic_store_explicit(&zlog_ec, enable, memory_order_relaxed); diff --git a/lib/zlog.h b/lib/zlog.h index 6e84fe8923..a530c589a8 100644 --- a/lib/zlog.h +++ b/lib/zlog.h @@ -183,8 +183,11 @@ extern void zlog_msg_args(struct zlog_msg *msg, size_t *hdrlen, /* default is local time zone */ #define ZLOG_TS_UTC (1 << 10) +struct timespec; + extern size_t zlog_msg_ts(struct zlog_msg *msg, struct fbuf *out, uint32_t flags); +extern void zlog_msg_tsraw(struct zlog_msg *msg, struct timespec *ts); /* "mmm dd hh:mm:ss" for RFC3164 syslog. Only ZLOG_TS_UTC for flags. */ extern size_t zlog_msg_ts_3164(struct zlog_msg *msg, struct fbuf *out, diff --git a/lib/zlog_5424.c b/lib/zlog_5424.c index 740d6bfba8..9da7c55fc5 100644 --- a/lib/zlog_5424.c +++ b/lib/zlog_5424.c @@ -799,7 +799,7 @@ static void zlog_5424_cycle(struct zlog_cfg_5424 *zcf, int fd) rcu_free(MTYPE_LOG_5424, oldt, zt.rcu_head); } -static int zlog_5424_reconnect(struct thread *t) +static void zlog_5424_reconnect(struct thread *t) { struct zlog_cfg_5424 *zcf = THREAD_ARG(t); int fd = THREAD_FD(t); @@ -812,7 +812,7 @@ static int zlog_5424_reconnect(struct thread *t) /* logger is sending us something?!?! */ thread_add_read(t->master, zlog_5424_reconnect, zcf, fd, &zcf->t_reconnect); - return 0; + return; } if (ret == 0) @@ -832,7 +832,6 @@ static int zlog_5424_reconnect(struct thread *t) frr_with_mutex (&zcf->cfg_mtx) { zlog_5424_cycle(zcf, fd); } - return 0; } static int zlog_5424_unix(struct sockaddr_un *suna, int sock_type) diff --git a/lib/zlog_live.c b/lib/zlog_live.c new file mode 100644 index 0000000000..fbe0e5ee49 --- /dev/null +++ b/lib/zlog_live.c @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2019-22 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "zebra.h" + +#include "zlog_live.h" + +#include "memory.h" +#include "frrcu.h" +#include "zlog.h" +#include "printfrr.h" + +DEFINE_MTYPE_STATIC(LOG, LOG_LIVE, "log vtysh live target"); + +enum { + STATE_NORMAL = 0, + STATE_FD_DEAD, + STATE_DISOWNED, +}; + +struct zlt_live { + struct zlog_target zt; + + atomic_uint_fast32_t fd; + struct rcu_head_close head_close; + struct rcu_head head_self; + + atomic_uint_fast32_t state; +}; + +static void zlog_live(struct zlog_target *zt, struct zlog_msg *msgs[], + size_t nmsgs) +{ + struct zlt_live *zte = container_of(zt, struct zlt_live, zt); + struct zlog_live_hdr hdrs[nmsgs], *hdr = hdrs; + struct mmsghdr mmhs[nmsgs], *mmh = mmhs; + struct iovec iovs[nmsgs * 3], *iov = iovs; + struct timespec ts; + size_t i, textlen; + int fd; + uint_fast32_t state; + + fd = atomic_load_explicit(&zte->fd, memory_order_relaxed); + + if (fd < 0) + return; + + memset(mmhs, 0, sizeof(mmhs)); + memset(hdrs, 0, sizeof(hdrs)); + + for (i = 0; i < nmsgs; i++) { + const struct fmt_outpos *argpos; + size_t n_argpos, arghdrlen; + struct zlog_msg *msg = msgs[i]; + int prio = zlog_msg_prio(msg); + + if (prio > zt->prio_min) + continue; + + zlog_msg_args(msg, &arghdrlen, &n_argpos, &argpos); + + mmh->msg_hdr.msg_iov = iov; + + iov->iov_base = hdr; + iov->iov_len = sizeof(*hdr); + iov++; + + if (n_argpos) { + iov->iov_base = (char *)argpos; + iov->iov_len = sizeof(*argpos) * n_argpos; + iov++; + } + + iov->iov_base = (char *)zlog_msg_text(msg, &textlen); + iov->iov_len = textlen; + iov++; + + zlog_msg_tsraw(msg, &ts); + + hdr->ts_sec = ts.tv_sec; + hdr->ts_nsec = ts.tv_nsec; + hdr->prio = zlog_msg_prio(msg); + hdr->flags = 0; + hdr->textlen = textlen; + hdr->arghdrlen = arghdrlen; + hdr->n_argpos = n_argpos; + + mmh->msg_hdr.msg_iovlen = iov - mmh->msg_hdr.msg_iov; + mmh++; + hdr++; + } + + size_t msgtotal = mmh - mmhs; + ssize_t sent; + + for (size_t msgpos = 0; msgpos < msgtotal; msgpos += sent) { + sent = sendmmsg(fd, mmhs + msgpos, msgtotal - msgpos, 0); + + if (sent <= 0) + goto out_err; + } + return; + +out_err: + fd = atomic_exchange_explicit(&zte->fd, -1, memory_order_relaxed); + if (fd < 0) + return; + + rcu_close(&zte->head_close, fd); + zlog_target_replace(zt, NULL); + + state = STATE_NORMAL; + atomic_compare_exchange_strong_explicit( + &zte->state, &state, STATE_FD_DEAD, memory_order_relaxed, + memory_order_relaxed); + if (state == STATE_DISOWNED) + rcu_free(MTYPE_LOG_LIVE, zte, head_self); +} + +static void zlog_live_sigsafe(struct zlog_target *zt, const char *text, + size_t len) +{ + struct zlt_live *zte = container_of(zt, struct zlt_live, zt); + struct zlog_live_hdr hdr[1]; + struct iovec iovs[2], *iov = iovs; + struct timespec ts; + int fd; + + fd = atomic_load_explicit(&zte->fd, memory_order_relaxed); + if (fd < 0) + return; + + clock_gettime(CLOCK_MONOTONIC, &ts); + + hdr->ts_sec = ts.tv_sec; + hdr->ts_nsec = ts.tv_nsec; + hdr->prio = LOG_CRIT; + hdr->flags = 0; + hdr->textlen = len; + hdr->n_argpos = 0; + + iov->iov_base = (char *)hdr; + iov->iov_len = sizeof(hdr); + iov++; + + iov->iov_base = (char *)text; + iov->iov_len = len; + iov++; + + writev(fd, iovs, iov - iovs); +} + +void zlog_live_open(struct zlog_live_cfg *cfg, int prio_min, int *other_fd) +{ + int sockets[2]; + struct zlt_live *zte; + struct zlog_target *zt; + + if (cfg->target) + zlog_live_close(cfg); + + *other_fd = -1; + if (prio_min == ZLOG_DISABLED) + return; + + /* the only reason for SEQPACKET here is getting close notifications. + * otherwise if you open a bunch of vtysh connections with live logs + * and close them all, the fds will stick around until we get an error + * when trying to log something to them at some later point -- which + * eats up fds and might be *much* later for some daemons. + */ + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets) < 0) { + if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets) < 0) { + zlog_warn("%% could not open socket pair: %m"); + return; + } + } else + /* SEQPACKET only: try to zap read direction */ + shutdown(sockets[0], SHUT_RD); + + *other_fd = sockets[1]; + + zt = zlog_target_clone(MTYPE_LOG_LIVE, NULL, sizeof(*zte)); + zte = container_of(zt, struct zlt_live, zt); + cfg->target = zte; + + zte->fd = sockets[0]; + zte->zt.prio_min = prio_min; + zte->zt.logfn = zlog_live; + zte->zt.logfn_sigsafe = zlog_live_sigsafe; + + zlog_target_replace(NULL, zt); +} + +void zlog_live_close(struct zlog_live_cfg *cfg) +{ + struct zlt_live *zte; + int fd; + + if (!cfg->target) + return; + + zte = cfg->target; + cfg->target = NULL; + + fd = atomic_exchange_explicit(&zte->fd, -1, memory_order_relaxed); + + if (fd >= 0) { + rcu_close(&zte->head_close, fd); + zlog_target_replace(&zte->zt, NULL); + } + rcu_free(MTYPE_LOG_LIVE, zte, head_self); +} + +void zlog_live_disown(struct zlog_live_cfg *cfg) +{ + struct zlt_live *zte; + uint_fast32_t state; + + if (!cfg->target) + return; + + zte = cfg->target; + cfg->target = NULL; + + state = STATE_NORMAL; + atomic_compare_exchange_strong_explicit( + &zte->state, &state, STATE_DISOWNED, memory_order_relaxed, + memory_order_relaxed); + if (state == STATE_FD_DEAD) + rcu_free(MTYPE_LOG_LIVE, zte, head_self); +} diff --git a/lib/zlog_live.h b/lib/zlog_live.h new file mode 100644 index 0000000000..c948baeab1 --- /dev/null +++ b/lib/zlog_live.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2019-22 David Lamparter, for NetDEF, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _FRR_ZLOG_LIVE_H +#define _FRR_ZLOG_LIVE_H + +#include "printfrr.h" + +struct zlog_live_hdr { + uint64_t ts_sec; + uint32_t ts_nsec; + uint32_t prio; + uint32_t flags; + uint32_t textlen; + + uint32_t arghdrlen; + uint32_t n_argpos; + struct fmt_outpos argpos[0]; +}; + +struct zlt_live; + +struct zlog_live_cfg { + struct zlt_live *target; + + /* nothing else here */ +}; + +extern void zlog_live_open(struct zlog_live_cfg *cfg, int prio_min, + int *other_fd); + +static inline bool zlog_live_is_null(struct zlog_live_cfg *cfg) +{ + return cfg->target == NULL; +} + +extern void zlog_live_close(struct zlog_live_cfg *cfg); +extern void zlog_live_disown(struct zlog_live_cfg *cfg); + +#endif /* _FRR_ZLOG_5424_H */ |
