MonClient/sub_want&renew_subs

本文深入探讨了Ceph Monitor的订阅机制,详细讲解了sub_want和renew_subs方法的实现细节,包括如何注入订阅请求,以及如何更新已发送的订阅状态。通过分析源代码,我们了解到sub_new和sub_sent数据结构的作用,以及它们在订阅流程中的关键作用。

MonClient/sub_want&renew_subs

文件

:MonClient/sub_want&renew_subs

方法:

bool sub_want(string what, version_t start, unsigned flags)
void renew_subs()

涉及的方法:

bool _sub_want(string what, version_t start, unsigned flags)
void _renew_subs()

涉及的数据结构:

map<string,ceph_mon_subscribe_item> sub_new;
map<string,ceph_mon_subscribe_item> sub_sent;
  • sub_new中保存的是想要订阅的map/version集合,但是还没有想monitor发起订阅请求
  • sub_sent保存的是已经向monitor发起的map/version集合

细节实现:

在sub_want获取monc_lock锁,主要逻辑都下方到_sub_want中。

bool sub_want(string what, version_t start, unsigned flags) {                                                                                                   
  Mutex::Locker l(monc_lock);                                                                                                                                   
  return _sub_want(what, start, flags);                                                                                                                         
}

向sub_new中注入要订阅的map/version;其主要逻辑是判断要订阅的map/version是否已经在sub_new或者sub_sent中,如果存在就返回false,
否则就将要订阅的map/version添加到sub_new中,并返回true

bool _sub_want(string what, version_t start, unsigned flags) {
  if ((sub_new.count(what) == 0 &&
       sub_sent.count(what) &&
       sub_sent[what].start == start &&
       sub_sent[what].flags == flags) ||
      (sub_new.count(what) &&
       sub_new[what].start == start &&
       sub_new[what].flags == flags))
    return false;
  sub_new[what].start = start;
  sub_new[what].flags = flags;
  return true;
}

将sub_new中要订阅的map/version,发送给monitor服务,并将sub_new中的元素合并到sub_sent中,再清空sub_new。

void renew_subs() {
  Mutex::Locker l(monc_lock);
  _renew_subs();
}

void MonClient::_renew_subs()
{
  assert(monc_lock.is_locked());
  if (sub_new.empty()) {
    ldout(cct, 10) << "renew_subs - empty" << dendl;
    return;
  }

  ldout(cct, 10) << "renew_subs" << dendl;
  if (cur_mon.empty())
    _reopen_session();
  else {
    if (sub_renew_sent == utime_t())
      sub_renew_sent = ceph_clock_now(cct);

    MMonSubscribe *m = new MMonSubscribe;
    m->what = sub_new;
    _send_mon_message(m);

    // update sub_sent with sub_new
    sub_new.insert(sub_sent.begin(), sub_sent.end());
    std::swap(sub_new, sub_sent);
    sub_new.clear();
  }
}
解读以下代码“int udhcpc_main(int argc UNUSED_PARAM, char **argv) { uint8_t *message; const char *str_V, *str_F, *str_r; IF_FEATURE_UDHCPC_ARPING(const char *str_a = "2000";) IF_FEATURE_UDHCP_PORT(char *str_P;) uint8_t *clientid_mac_ptr; llist_t *list_O = NULL; llist_t *list_x = NULL; int tryagain_timeout = 20; int discover_timeout = 3; int discover_retries = 3; uint32_t server_id = server_id; /* for compiler */ uint32_t requested_ip = 0; int packet_num; int timeout; /* must be signed */ int lease_remaining; /* must be signed */ unsigned opt; IF_FEATURE_UDHCPC_ARPING(unsigned arpping_ms;) int retval; setup_common_bufsiz(); /* Default options */ IF_FEATURE_UDHCP_PORT(SERVER_PORT = 67;) IF_FEATURE_UDHCP_PORT(CLIENT_PORT = 68;) client_data.interface = CONFIG_UDHCPC_DEFAULT_INTERFACE; client_data.script = CONFIG_UDHCPC_DEFAULT_SCRIPT; client_data.sockfd = -1; str_V = "udhcp "BB_VER; /* Make sure fd 0,1,2 are open */ /* Set up the signal pipe on fds 3,4 - must be before openlog() */ udhcp_sp_setup(); /* Parse command line */ opt = getopt32long(argv, "^" /* O,x: list; -T,-t,-A take numeric param */ "CV:F:i:np:qRr:s:T:+t:+SA:+O:*ox:*fB" USE_FOR_MMU("b") IF_FEATURE_UDHCPC_ARPING("a::") IF_FEATURE_UDHCP_PORT("P:") "v" "\0" IF_UDHCP_VERBOSE("vv") /* -v is a counter */ , udhcpc_longopts , &str_V, &str_F , &client_data.interface, &client_data.pidfile /* i,p */ , &str_r /* r */ , &client_data.script /* s */ , &discover_timeout, &discover_retries, &tryagain_timeout /* T,t,A */ , &list_O , &list_x IF_FEATURE_UDHCPC_ARPING(, &str_a) IF_FEATURE_UDHCP_PORT(, &str_P) IF_UDHCP_VERBOSE(, &dhcp_verbose) ); if (opt & OPT_F) { char *p; unsigned len; /* FQDN option format: [0x51][len][flags][0][0]<fqdn> */ len = strlen(str_F); p = udhcp_insert_new_option( &client_data.options, DHCP_FQDN, len + 3, /*dhcp6:*/ 0); /* Flag bits: 0000NEOS * S: 1 = Client requests server to update A RR in DNS as well as PTR * O: 1 = Server indicates to client that DNS has been updated regardless * E: 1 = Name is in DNS format, i.e. <4>host<6>domain<3>com<0>, * not "host.domain.com". Format 0 is obsolete. * N: 1 = Client requests server to not update DNS (S must be 0 then) * Two [0] bytes which follow are deprecated and must be 0. */ p[OPT_DATA + 0] = 0x1; /*p[OPT_DATA + 1] = 0; - xzalloc did it */ /*p[OPT_DATA + 2] = 0; */ memcpy(p + OPT_DATA + 3, str_F, len); /* do not store NUL byte */ } if (opt & OPT_r) if (!inet_aton(str_r, (void*)&requested_ip)) bb_show_usage(); #if ENABLE_FEATURE_UDHCP_PORT if (opt & OPT_P) { CLIENT_PORT = xatou16(str_P); SERVER_PORT = CLIENT_PORT - 1; } #endif IF_FEATURE_UDHCPC_ARPING(arpping_ms = xatou(str_a);) while (list_O) { char *optstr = llist_pop(&list_O); unsigned n = bb_strtou(optstr, NULL, 0); if (errno || n > 254) { n = udhcp_option_idx(optstr, dhcp_option_strings); n = dhcp_optflags[n].code; } client_data.opt_mask[n >> 3] |= 1 << (n & 7); } if (!(opt & OPT_o)) { unsigned i, n; for (i = 0; (n = dhcp_optflags[i].code) != 0; i++) { if (dhcp_optflags[i].flags & OPTION_REQ) { client_data.opt_mask[n >> 3] |= 1 << (n & 7); } } } while (list_x) { char *optstr = xstrdup(llist_pop(&list_x)); udhcp_str2optset(optstr, &client_data.options, dhcp_optflags, dhcp_option_strings, /*dhcpv6:*/ 0 ); free(optstr); } if (str_V[0] != '\0') { char *p; unsigned len = strnlen(str_V, 254); p = udhcp_insert_new_option( &client_data.options, DHCP_VENDOR, len, /*dhcp6:*/ 0); memcpy(p + OPT_DATA, str_V, len); /* do not store NUL byte */ } clientid_mac_ptr = NULL; if (!(opt & OPT_C) && !udhcp_find_option(client_data.options, DHCP_CLIENT_ID, /*dhcpv6:*/ 0)) { /* not suppressed and not set, create default client ID */ clientid_mac_ptr = udhcp_insert_new_option( &client_data.options, DHCP_CLIENT_ID, 1 + 6, /*dhcp6:*/ 0); clientid_mac_ptr[OPT_DATA] = 1; /* type: ethernet */ clientid_mac_ptr += OPT_DATA + 1; /* skip option code, len, ethernet */ } /* Not really necessary (we redo it on every iteration) * but allows early (before daemonization) detection * of bad interface name. */ if (udhcp_read_interface(client_data.interface, &client_data.ifindex, NULL, client_data.client_mac) ) { return 1; } #if !BB_MMU /* on NOMMU reexec (i.e., background) early */ if (!(opt & OPT_f)) { bb_daemonize_or_rexec(0 /* flags */, argv); logmode = LOGMODE_NONE; } #endif if (opt & OPT_S) { openlog(applet_name, LOG_PID, LOG_DAEMON); logmode |= LOGMODE_SYSLOG; } /* Create pidfile */ write_pidfile(client_data.pidfile); /* Goes to stdout (unless NOMMU) and possibly syslog */ bb_simple_info_msg("started, v"BB_VER); /* We want random_xid to be random... */ srand(monotonic_us()); client_data.state = INIT_SELECTING; d4_run_script_deconfig(); packet_num = 0; timeout = 0; lease_remaining = 0; /* Main event loop. select() waits on signal pipe and possibly * on sockfd. * "continue" statements in code below jump to the top of the loop. */ for (;;) { struct pollfd pfds[2]; struct dhcp_packet packet; //bb_error_msg("sockfd:%d, listen_mode:%d", client_data.sockfd, client_data.listen_mode); /* Was opening raw or udp socket here * if (client_data.listen_mode != LISTEN_NONE && client_data.sockfd < 0), * but on fast network renew responses return faster * than we open sockets. Thus this code is moved * to change_listen_mode(). Thus we open listen socket * BEFORE we send renew request (see "case BOUND:"). */ udhcp_sp_fd_set(pfds, client_data.sockfd); retval = 0; /* If we already timed out, fall through with retval = 0, else... */ if (timeout > 0) { unsigned diff; if (timeout > INT_MAX/1000) timeout = INT_MAX/1000; log1("waiting %u seconds", timeout); diff = (unsigned)monotonic_sec(); retval = poll(pfds, 2, timeout * 1000); diff = (unsigned)monotonic_sec() - diff; lease_remaining -= diff; if (lease_remaining < 0) lease_remaining = 0; timeout -= diff; if (timeout < 0) timeout = 0; if (retval < 0) { /* EINTR? A signal was caught, don't panic */ if (errno == EINTR) { continue; } /* Else: an error occurred, panic! */ bb_simple_perror_msg_and_die("poll"); } } /* If timeout dropped to zero, time to become active: * resend discover/renew/whatever */ if (retval == 0) { /* When running on a bridge, the ifindex may have changed * (e.g. if member interfaces were added/removed * or if the status of the bridge changed). * Refresh ifindex and client_mac: */ if (udhcp_read_interface(client_data.interface, &client_data.ifindex, NULL, client_data.client_mac) ) { goto ret0; /* iface is gone? */ } if (clientid_mac_ptr) memcpy(clientid_mac_ptr, client_data.client_mac, 6); switch (client_data.state) { case INIT_SELECTING: if (!discover_retries || packet_num < discover_retries) { if (packet_num == 0) { change_listen_mode(LISTEN_RAW); client_data.xid = random_xid(); } /* broadcast */ send_discover(requested_ip); timeout = discover_timeout; packet_num++; continue; } leasefail: change_listen_mode(LISTEN_NONE); d4_run_script(NULL, "leasefail"); #if BB_MMU /* -b is not supported on NOMMU */ if (opt & OPT_b) { /* background if no lease */ bb_simple_info_msg("no lease, forking to background"); client_background(); /* do not background again! */ opt = ((opt & ~(OPT_b|OPT_n)) | OPT_f); /* ^^^ also disables -n (-b takes priority over -n): * ifup's default udhcpc options are -R -n, * and users want to be able to add -b * (in a config file) to make it background * _and not exit_. */ } else #endif if (opt & OPT_n) { /* abort if no lease */ bb_simple_info_msg("no lease, failing"); retval = 1; goto ret; } /* Wait before trying again */ timeout = tryagain_timeout; packet_num = 0; continue; case REQUESTING: if (packet_num < 3) { /* send broadcast select packet */ send_select(server_id, requested_ip); timeout = discover_timeout; packet_num++; continue; } /* Timed out, go back to init state. * "discover...select...discover..." loops * were seen in the wild. Treat them similarly * to "no response to discover" case */ client_data.state = INIT_SELECTING; goto leasefail; case BOUND: /* 1/2 lease passed, enter renewing state */ client_data.state = RENEWING; client_data.first_secs = 0; /* make secs field count from 0 */ got_SIGUSR1: log1s("entering renew state"); change_listen_mode(LISTEN_KERNEL); /* fall right through */ case RENEW_REQUESTED: /* in manual (SIGUSR1) renew */ case RENEWING: if (packet_num == 0) { /* Send an unicast renew request */ /* Sometimes observed to fail (EADDRNOTAVAIL) to bind * a new UDP socket for sending inside send_renew. * I hazard to guess existing listening socket * is somehow conflicting with it, but why is it * not deterministic then?! Strange. * Anyway, it does recover by eventually failing through * into INIT_SELECTING state. */ if (send_renew(server_id, requested_ip) >= 0) { timeout = discover_timeout; packet_num++; continue; } /* else: error sending. * example: ENETUNREACH seen with server * which gave us bogus server ID 1.1.1.1 * which wasn't reachable (and probably did not exist). */ } /* else: we had sent one packet, but got no reply */ log1s("no response to renew"); if (lease_remaining > 30) { /* Some lease time remains, try to renew later */ change_listen_mode(LISTEN_NONE); goto BOUND_for_half_lease; } /* Enter rebinding state */ client_data.state = REBINDING; log1s("entering rebinding state"); /* Switch to bcast receive */ change_listen_mode(LISTEN_RAW); packet_num = 0; /* fall right through */ case REBINDING: /* Lease is *really* about to run out, * try to find DHCP server using broadcast */ if (lease_remaining > 0 && packet_num < 3) { /* send a broadcast renew request */ send_renew(0 /*INADDR_ANY*/, requested_ip); timeout = discover_timeout; packet_num++; continue; } /* Timed out, enter init state */ change_listen_mode(LISTEN_NONE); bb_simple_info_msg("lease lost, entering init state"); d4_run_script_deconfig(); client_data.state = INIT_SELECTING; client_data.first_secs = 0; /* make secs field count from 0 */ timeout = 0; packet_num = 0; continue; /* case RELEASED: */ } /* RELEASED state (when we got SIGUSR2) ends up here. * (wait for SIGUSR1 to re-init, or for TERM, etc) */ timeout = INT_MAX; continue; /* back to main loop */ } /* if poll timed out */ /* poll() didn't timeout, something happened */ /* Is it a signal? */ switch (udhcp_sp_read()) { case SIGUSR1: if (client_data.state <= REQUESTING) /* Initial negotiations in progress, do not disturb */ break; if (client_data.state == REBINDING) /* Do not go back from rebind to renew state */ break; if (lease_remaining > 30) /* if renew fails, do not go back to BOUND */ lease_remaining = 30; client_data.first_secs = 0; /* make secs field count from 0 */ packet_num = 0; switch (client_data.state) { case BOUND: case RENEWING: /* Try to renew/rebind */ client_data.state = RENEW_REQUESTED; goto got_SIGUSR1; case RENEW_REQUESTED: /* Two SIGUSR1 received, start things over */ change_listen_mode(LISTEN_NONE); d4_run_script_deconfig(); default: /* case RELEASED: */ /* Wake from SIGUSR2-induced deconfigured state */ change_listen_mode(LISTEN_NONE); } client_data.state = INIT_SELECTING; /* Kill any timeouts, user wants this to hurry along */ timeout = 0; continue; case SIGUSR2: perform_release(server_id, requested_ip); /* ^^^ switches to LISTEN_NONE */ timeout = INT_MAX; continue; case SIGTERM: bb_info_msg("received %s", "SIGTERM"); goto ret0; } /* Is it a packet? */ if (!pfds[1].revents) continue; /* no */ { int len; /* A packet is ready, read it */ if (client_data.listen_mode == LISTEN_KERNEL) len = udhcp_recv_kernel_packet(&packet, client_data.sockfd); else len = d4_recv_raw_packet(&packet, client_data.sockfd); if (len == -1) { /* Error is severe, reopen socket */ bb_error_msg("read error: "STRERROR_FMT", reopening socket" STRERROR_ERRNO); sleep(discover_timeout); /* 3 seconds by default */ change_listen_mode(client_data.listen_mode); /* just close and reopen */ } if (len < 0) continue; } if (packet.xid != client_data.xid) { log1("xid %x (our is %x)%s", (unsigned)packet.xid, (unsigned)client_data.xid, ", ignoring packet" ); continue; } /* Ignore packets that aren't for us */ if (packet.hlen != 6 || memcmp(packet.chaddr, client_data.client_mac, 6) != 0 ) { //FIXME: need to also check that last 10 bytes are zero log1("chaddr does not match%s", ", ignoring packet"); continue; } message = udhcp_get_option(&packet, DHCP_MESSAGE_TYPE); if (message == NULL) { log1("no message type option%s", ", ignoring packet"); continue; } switch (client_data.state) { case INIT_SELECTING: /* Must be a DHCPOFFER */ if (*message == DHCPOFFER) { struct in_addr temp_addr; uint8_t *temp; /* What exactly is server's IP? There are several values. * Example DHCP offer captured with tchdump: * * 10.34.25.254:67 > 10.34.25.202:68 // IP header's src * BOOTP fields: * Your-IP 10.34.25.202 * Server-IP 10.34.32.125 // "next server" IP * Gateway-IP 10.34.25.254 // relay's address (if DHCP relays are in use) * DHCP options: * DHCP-Message Option 53, length 1: Offer * Server-ID Option 54, length 4: 10.34.255.7 // "server ID" * Default-Gateway Option 3, length 4: 10.34.25.254 // router * * We think that real server IP (one to use in renew/release) * is one in Server-ID option. But I am not 100% sure. * IP header's src and Gateway-IP (same in this example) * might work too. * "Next server" and router are definitely wrong ones to use, though... */ /* We used to ignore packets without DHCP_SERVER_ID. * I've got user reports from people who run "address-less" servers. * They either supply DHCP_SERVER_ID of 0.0.0.0 or don't supply it at all. * They say ISC DHCP client supports this case. */ server_id = 0; temp = udhcp_get_option32(&packet, DHCP_SERVER_ID); if (!temp) { bb_simple_info_msg("no server ID, using 0.0.0.0"); } else { /* it IS unaligned sometimes, don't "optimize" */ move_from_unaligned32(server_id, temp); } /*xid = packet.xid; - already is */ temp_addr.s_addr = requested_ip = packet.yiaddr; log1("received offer of %s", inet_ntoa(temp_addr)); /* enter requesting state */ client_data.state = REQUESTING; timeout = 0; packet_num = 0; } continue; case REQUESTING: case RENEWING: case RENEW_REQUESTED: case REBINDING: if (*message == DHCPACK) { unsigned start; struct in_addr temp_addr; char server_str[sizeof("255.255.255.255")]; uint8_t *temp; change_listen_mode(LISTEN_NONE); temp_addr.s_addr = server_id; strcpy(server_str, inet_ntoa(temp_addr)); temp_addr.s_addr = packet.yiaddr; lease_remaining = 60 * 60; temp = udhcp_get_option32(&packet, DHCP_LEASE_TIME); if (temp) { uint32_t lease; /* it IS unaligned sometimes, don't "optimize" */ move_from_unaligned32(lease, temp); lease_remaining = ntohl(lease); } /* Log message _before_ we sanitize lease */ bb_info_msg("lease of %s obtained from %s, lease time %u%s", inet_ntoa(temp_addr), server_str, (unsigned)lease_remaining, temp ? "" : " (default)" ); /* paranoia: must not be too small and not prone to overflows */ /* NB: 60s leases _are_ used in real world * (temporary IPs while ISP modem initializes) * do not break this case by bumping it up. */ if (lease_remaining < 0) /* signed overflow? */ lease_remaining = INT_MAX; if (lease_remaining < 30) lease_remaining = 30; requested_ip = packet.yiaddr; #if ENABLE_FEATURE_UDHCPC_ARPING if (opt & OPT_a) { /* RFC 2131 3.1 paragraph 5: * "The client receives the DHCPACK message with configuration * parameters. The client SHOULD perform a final check on the * parameters (e.g., ARP for allocated network address), and notes * the duration of the lease specified in the DHCPACK message. At this * point, the client is configured. If the client detects that the * address is already in use (e.g., through the use of ARP), * the client MUST send a DHCPDECLINE message to the server and restarts * the configuration process..." */ if (!arpping(requested_ip, NULL, (uint32_t) 0, client_data.client_mac, client_data.interface, arpping_ms) ) { bb_simple_info_msg("offered address is in use " "(got ARP reply), declining"); client_data.xid = random_xid(); //TODO: can omit? send_decline(server_id, packet.yiaddr); if (client_data.state != REQUESTING) d4_run_script_deconfig(); client_data.state = INIT_SELECTING; client_data.first_secs = 0; /* make secs field count from 0 */ requested_ip = 0; timeout = tryagain_timeout; packet_num = 0; continue; /* back to main loop */ } } #endif /* enter bound state */ start = monotonic_sec(); d4_run_script(&packet, client_data.state == REQUESTING ? "bound" : "renew"); lease_remaining -= (unsigned)monotonic_sec() - start; if (lease_remaining < 0) lease_remaining = 0; if (opt & OPT_q) { /* quit after lease */ goto ret0; } /* future renew failures should not exit (JM) */ opt &= ~OPT_n; #if BB_MMU /* NOMMU case backgrounded earlier */ if (!(opt & OPT_f)) { client_background(); /* do not background again! */ opt = ((opt & ~OPT_b) | OPT_f); } #endif BOUND_for_half_lease: timeout = (unsigned)lease_remaining / 2; client_data.state = BOUND; /* make future renew packets use different xid */ /* client_data.xid = random_xid(); ...but why bother? */ packet_num = 0; continue; /* back to main loop */ } if (*message == DHCPNAK) { /* If network has more than one DHCP server, * "wrong" server can reply first, with a NAK. * Do not interpret it as a NAK from "our" server. */ uint32_t svid = 0; /* we treat no server id as 0.0.0.0 */ uint8_t *temp = udhcp_get_option32(&packet, DHCP_SERVER_ID); if (temp) move_from_unaligned32(svid, temp); if (svid != server_id) { log1("received DHCP NAK with wrong" " server ID%s", ", ignoring packet"); continue; } /* return to init state */ change_listen_mode(LISTEN_NONE); bb_info_msg("received %s", "DHCP NAK"); d4_run_script(&packet, "nak"); if (client_data.state != REQUESTING) d4_run_script_deconfig(); sleep(3); /* avoid excessive network traffic */ client_data.state = INIT_SELECTING; client_data.first_secs = 0; /* make secs field count from 0 */ requested_ip = 0; timeout = 0; packet_num = 0; } continue; /* case BOUND: - ignore all packets */ /* case RELEASED: - ignore all packets */ } /* back to main loop */ } /* for (;;) - main loop ends */ ret0: if (opt & OPT_R) /* release on quit */ perform_release(server_id, requested_ip); retval = 0; ret: /*if (client_data.pidfile) - remove_pidfile has its own check */ remove_pidfile(client_data.pidfile); return retval; }”
最新发布
09-12
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值