Home | History | Annotate | Line # | Download | only in ServiceRegistration
      1 /* posix.c
      2  *
      3  * Copyright (c) 2018-2021 Apple, Inc. All rights reserved.
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *     https://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  *
     17  * utility functions common to all posix implementations (e.g., MacOS, Linux).
     18  */
     19 
     20 #define _GNU_SOURCE
     21 
     22 #include <netinet/in.h>
     23 #include <net/if.h>
     24 #ifndef LINUX
     25 #include <netinet/in_var.h>
     26 #include <net/if_dl.h>
     27 #endif
     28 #include <sys/ioctl.h>
     29 #include <errno.h>
     30 #include <ifaddrs.h>
     31 #include <stdlib.h>
     32 #include <string.h>
     33 #include <dirent.h>
     34 #include <arpa/inet.h>
     35 #include "dns_sd.h"
     36 #include "srp.h"
     37 #include "dns-msg.h"
     38 #include "ioloop.h"
     39 #ifdef SRP_TEST_SERVER
     40 #include "test-api.h"
     41 #endif
     42 
     43 #undef OBJECT_TYPE
     44 #define OBJECT_TYPE(x) int x##_created, x##_finalized, old_##x##_created, old_##x##_finalized;
     45 #include "object-types.h"
     46 
     47 void
     48 ioloop_dump_object_allocation_stats(void)
     49 {
     50 #undef OBJECT_TYPE
     51 #define OBJECT_TYPE(x) || (x ## _created != old_ ## x ## _created) || (x ## _finalized != old_ ## x ## _finalized)
     52     if (false
     53 #include "object-types.h"
     54         )
     55     {
     56         char outbuf[1000];
     57         char *obp = outbuf;
     58         size_t len;
     59 #undef OBJECT_TYPE
     60 #define OBJECT_TYPE_STR(x) #x
     61 #define OBJECT_TYPE(x)                                                                                  \
     62         len = snprintf(obp, (sizeof(outbuf)) - (obp - outbuf), OBJECT_TYPE_STR(x) " %d %d %d %d|",      \
     63              old_ ## x ##_created, x ## _created, old_ ## x ## _finalized, x ## _finalized);            \
     64         obp += len;                                                                                     \
     65         old_ ## x ## _created = x ## _created;                                                          \
     66         old_ ## x ## _finalized = x ## _finalized;                                                      \
     67         if (obp - outbuf > 900) {                                                                       \
     68             INFO(PUB_S_SRP, outbuf);                                                                    \
     69             obp = outbuf;                                                                               \
     70             len = 0;                                                                                    \
     71         }
     72 #include "object-types.h"
     73         if (len > 0) {
     74             INFO(PUB_S_SRP, outbuf);
     75         }
     76     }
     77     int num_fds = get_num_fds();
     78     if (num_fds < 0) {
     79         FAULT("out of file descriptors!!");
     80         abort();
     81     }
     82     INFO("%d file descriptors in use", num_fds);
     83 }
     84 
     85 interface_address_state_t *interface_addresses;
     86 
     87 void
     88 ioloop_strcpy(char *dest, const char *src, size_t lim)
     89 {
     90     size_t len = strlen(src);
     91     if (len >= lim - 1) {
     92       len = lim - 1;
     93     }
     94     memcpy(dest, src, len);
     95     dest[len] = 0;
     96 }
     97 
     98 bool
     99 ioloop_map_interface_addresses(srp_server_t *server_state, const char *ifname, void *context,
    100                                interface_callback_t callback)
    101 {
    102     return ioloop_map_interface_addresses_here(server_state, &interface_addresses, ifname, context, callback);
    103 }
    104 
    105 static bool
    106 ioloop_same_address(struct sockaddr *a, addr_t *b, struct sockaddr *ma, addr_t *sk)
    107 {
    108     // If the family is different, addresses are definitely not the same
    109     if (a->sa_family != b->sa.sa_family) {
    110         return false;
    111     }
    112 
    113     // For IPv4 addresses, both the address and the netmask must match
    114     if (a->sa_family == AF_INET && b->sa.sa_family == AF_INET && ma->sa_family == AF_INET) {
    115         struct sockaddr_in *a4 = (struct sockaddr_in *)a;
    116         struct sockaddr_in *ma4 = (struct sockaddr_in *)ma;
    117 
    118         if (a4->sin_addr.s_addr == b->sin.sin_addr.s_addr && ma4->sin_addr.s_addr == sk->sin.sin_addr.s_addr) {
    119             return true;
    120         }
    121     }
    122 
    123     // For IPv6 adddresses, same deal
    124     else if (a->sa_family == AF_INET6 && b->sa.sa_family == AF_INET6 && ma->sa_family == AF_INET6) {
    125         struct sockaddr_in6 *a6 = (struct sockaddr_in6 *)a;
    126         struct sockaddr_in6 *ma6 = (struct sockaddr_in6 *)ma;
    127 
    128         if (!memcmp(&a6->sin6_addr, &b->sin6.sin6_addr, sizeof b->sin6.sin6_addr) &&
    129             !memcmp(&ma6->sin6_addr, &sk->sin6.sin6_addr, sizeof sk->sin6.sin6_addr))
    130         {
    131             return true;
    132         }
    133     }
    134 #ifndef LINUX
    135     // For AF_LINK addresses, there is no netmask, and we are assuming a 6-byte ethernet address.
    136     else if (a->sa_family == AF_LINK && b->sa.sa_family == AF_LINK) {
    137         struct sockaddr_dl *sdl = (struct sockaddr_dl *)a;
    138         if (sdl->sdl_alen == 6 && !memcmp(LLADDR(sdl), b->ether_addr.addr, 6) && b->ether_addr.index == sdl->sdl_index)
    139         {
    140             return true;
    141         }
    142     }
    143 #endif
    144 
    145     return false; // Unknown address family, don't know how to compare, don't really care.
    146 }
    147 
    148 bool
    149 ioloop_map_interface_addresses_here_(srp_server_t *server_state, interface_address_state_t **here, const char *ifname,
    150                                      void *context, interface_callback_t callback, const char *file, int line)
    151 {
    152     struct ifaddrs *ifaddrs, *ifp;
    153     interface_address_state_t *kept_ifaddrs = NULL, **ki_end = &kept_ifaddrs;
    154     interface_address_state_t *new_ifaddrs = NULL, **ni_end = &new_ifaddrs;
    155     interface_address_state_t **ip, *nif;
    156 
    157 #ifdef SRP_TEST_SERVER
    158     int ret = srp_test_getifaddrs(server_state, &ifaddrs, context);
    159 #else
    160     int ret = getifaddrs(&ifaddrs);
    161 #endif
    162     if (ret < 0) {
    163         ERROR("getifaddrs failed: " PUB_S_SRP, strerror(errno));
    164         return false;
    165     }
    166 
    167     for (ifp = ifaddrs; ifp; ifp = ifp->ifa_next) {
    168         bool remove = false;
    169         bool keep = true;
    170 
    171         // It is impossible to have an interface without interface name.
    172         if (ifp->ifa_name == NULL) {
    173             continue;
    174         }
    175         if (ifname != NULL && strcmp(ifname, ifp->ifa_name)) {
    176             continue;
    177         }
    178 
    179 #ifndef LINUX
    180         // Check for temporary addresses, etc.
    181         if (ifp->ifa_addr != NULL && ifp->ifa_addr->sa_family == AF_INET6) {
    182             struct in6_ifreq ifreq;
    183             int sock;
    184             size_t len;
    185             len = strlen(ifp->ifa_name);
    186             if (len >= sizeof(ifreq.ifr_name)) {
    187                 len = sizeof(ifreq.ifr_name) - 1;
    188             }
    189             memcpy(ifreq.ifr_name, ifp->ifa_name, len);
    190             ifreq.ifr_name[len] = 0;
    191             if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
    192                 ERROR("socket(AF_INET6, SOCK_DGRAM): " PUB_S_SRP, strerror(errno));
    193                 continue;
    194             }
    195             memcpy(&ifreq.ifr_addr, ifp->ifa_addr, sizeof ifreq.ifr_addr);
    196             if (ioctl(sock, SIOCGIFAFLAG_IN6, &ifreq) < 0) {
    197                 ERROR("ioctl(SIOCGIFAFLAG_IN6): " PUB_S_SRP, strerror(errno));
    198                 close(sock);
    199                 continue;
    200             }
    201             int flags = ifreq.ifr_ifru.ifru_flags6;
    202             if (flags & (IN6_IFF_ANYCAST | IN6_IFF_TENTATIVE | IN6_IFF_DETACHED | IN6_IFF_TEMPORARY)) {
    203                 keep = false;
    204             }
    205             if (flags & IN6_IFF_DEPRECATED) {
    206                 remove = true;
    207             }
    208             close(sock);
    209         }
    210 
    211 #ifdef DEBUG_AF_LINK
    212         if (ifp->ifa_addr != NULL && ifp->ifa_addr->sa_family != AF_INET && ifp->ifa_addr->sa_family != AF_INET6) {
    213             struct sockaddr_dl *sdl = (struct sockaddr_dl *)ifp->ifa_addr;
    214             const uint8_t *addr = (uint8_t *)LLADDR(sdl);
    215             INFO("%.*s index %d alen %d dlen %d SDL: %02x:%02x:%02x:%02x:%02x:%02x",
    216                  sdl->sdl_nlen, sdl->sdl_data, sdl->sdl_index, sdl->sdl_alen, sdl->sdl_slen,
    217                  addr[0],  addr[1],  addr[2], addr[3],  addr[4],  addr[5]);
    218         }
    219 #endif // DEBUG_AF_LINK
    220 #endif // LINUX
    221 
    222         // Is this an interface address we can use?
    223         if (keep && ifp->ifa_addr != NULL && (
    224 #ifndef LINUX
    225                 ifp->ifa_addr->sa_family == AF_LINK ||
    226 #endif
    227              ((ifp->ifa_addr->sa_family == AF_INET6 || ifp->ifa_addr->sa_family == AF_INET) && ifp->ifa_netmask != NULL)) &&
    228             (ifp->ifa_flags & IFF_UP))
    229         {
    230             keep = false;
    231             for (ip = here; *ip != NULL; ) {
    232                 interface_address_state_t *ia = *ip;
    233                 // Same interface and address?
    234                 if (!remove && !strcmp(ia->name, ifp->ifa_name) &&
    235                     ioloop_same_address(ifp->ifa_addr, &ia->addr, ifp->ifa_netmask, &ia->mask))
    236                 {
    237                     *ip = ia->next;
    238                     *ki_end = ia;
    239                     ki_end = &ia->next;
    240                     ia->next = NULL;
    241                     keep = true;
    242                     break;
    243                 } else {
    244                     ip = &ia->next;
    245                 }
    246             }
    247             // If keep is false, this is a new interface/address.
    248             if (!keep) {
    249                 size_t len = strlen(ifp->ifa_name);
    250 #ifdef MALLOC_DEBUG_LOGGING
    251                 nif = debug_calloc(1, len + 1 + sizeof(*nif), file, line);
    252 #else
    253                 (void)file;
    254                 (void)line;
    255                 nif = calloc(1, len + 1 + sizeof(*nif));
    256 #endif
    257                 // We don't have a way to fix nif being null; what this means is that we don't detect a new
    258                 // interface address.
    259                 if (nif != NULL) {
    260                     nif->name = (char *)(nif + 1);
    261                     memcpy(nif->name, ifp->ifa_name, len);
    262                     nif->name[len] = 0;
    263                     if (ifp->ifa_addr->sa_family == AF_INET) {
    264                         nif->addr.sin = *((struct sockaddr_in *)ifp->ifa_addr);
    265                         nif->mask.sin = *((struct sockaddr_in *)ifp->ifa_netmask);
    266 
    267                         IPv4_ADDR_GEN_SRP(&nif->addr.sin.sin_addr.s_addr, __new_interface_ipv4_addr);
    268                         INFO("new IPv4 interface address added - ifname: " PUB_S_SRP
    269                              ", addr: " PRI_IPv4_ADDR_SRP, nif->name,
    270                              IPv4_ADDR_PARAM_SRP(&nif->addr.sin.sin_addr.s_addr, __new_interface_ipv4_addr));
    271                     } else if (ifp->ifa_addr->sa_family == AF_INET6) {
    272                         nif->addr.sin6 = *((struct sockaddr_in6 *)ifp->ifa_addr);
    273                         nif->mask.sin6 = *((struct sockaddr_in6 *)ifp->ifa_netmask);
    274 
    275                         SEGMENTED_IPv6_ADDR_GEN_SRP(nif->addr.sin6.sin6_addr.s6_addr, __new_interface_ipv6_addr);
    276                         INFO("new IPv6 interface address added - ifname: " PUB_S_SRP
    277                              ", addr: " PRI_SEGMENTED_IPv6_ADDR_SRP, nif->name,
    278                              SEGMENTED_IPv6_ADDR_PARAM_SRP(nif->addr.sin6.sin6_addr.s6_addr,
    279                                                            __new_interface_ipv6_addr));
    280                     } else {
    281 #ifndef LINUX
    282                         struct sockaddr_dl *sdl = (struct sockaddr_dl *)ifp->ifa_addr;
    283                         memset(&nif->mask, 0, sizeof(nif->mask));
    284                         if (sdl->sdl_alen == 6) {
    285                             nif->addr.ether_addr.len = 6;
    286                             memcpy(nif->addr.ether_addr.addr, LLADDR(sdl), 6);
    287                             nif->addr.ether_addr.index = sdl->sdl_index;
    288                             nif->addr.ether_addr.family = AF_LINK;
    289                         } else {
    290                             free(nif);
    291                             nif = NULL;
    292                         }
    293 
    294 #endif // LINUX
    295                     }
    296                     if (nif != NULL) {
    297                         nif->flags = ifp->ifa_flags;
    298                         *ni_end = nif;
    299                         ni_end = &nif->next;
    300                     }
    301                 }
    302             }
    303         }
    304     }
    305 
    306 #ifndef LINUX
    307     // Get rid of any link-layer addresses for which there is no other address on that interface
    308     // This is clunky, but we can't assume that the AF_LINK address will come after some other
    309     // address, so there's no more efficient way to do this that I can think of.
    310     for (ip = &new_ifaddrs; *ip; ) {
    311         if ((*ip)->addr.sa.sa_family == AF_LINK) {
    312             bool drop = true;
    313             // We need to iterate across both new_ifaddrs and kept_ifaddrs to find all of the addresses on an
    314             // interface. Only if there are no IP addresses on either list for the interface for which we have
    315             // the AF_LINK address do we drop the AF_LINK address.
    316             for (int q = 0; q < 2; q++) {
    317                 interface_address_state_t *list = q ? kept_ifaddrs : new_ifaddrs;
    318                 for (nif = list; nif; nif = nif->next) {
    319                     if (nif != *ip && nif->addr.sa.sa_family != AF_LINK && !strcmp(nif->name, (*ip)->name)) {
    320 #define TOO_MUCH_INFO
    321 #ifdef TOO_MUCH_INFO
    322                         char buf[INET6_ADDRSTRLEN];
    323                         if (nif->addr.sa.sa_family == AF_INET6) {
    324                             inet_ntop(AF_INET6, &nif->addr.sin6.sin6_addr, buf, sizeof(buf));
    325                         } else if (nif->addr.sa.sa_family == AF_INET) {
    326                             inet_ntop(AF_INET, &nif->addr.sin6.sin6_addr, buf, sizeof(buf));
    327                         }
    328                         INFO("new link-layer address not dropped because " PRI_S_SRP " - ifname: " PUB_S_SRP ", addr: "
    329                              PRI_MAC_ADDR_SRP, buf, (*ip)->name, MAC_ADDR_PARAM_SRP((*ip)->addr.ether_addr.addr));
    330 #endif // TOO_MUCH_INFO
    331                         drop = false;
    332                         break;
    333                     }
    334                 }
    335             }
    336             if (drop) {
    337 #ifdef TOO_MUCH_INFO
    338                 INFO("new link-layer interface address dropped - ifname: " PUB_S_SRP
    339                      ", addr: " PRI_MAC_ADDR_SRP, (*ip)->name, MAC_ADDR_PARAM_SRP((*ip)->addr.ether_addr.addr));
    340 #endif
    341                 nif = *ip;
    342                 *ip = nif->next;
    343                 free(nif);
    344             } else {
    345                 ip = &(*ip)->next;
    346             }
    347         } else {
    348             ip = &(*ip)->next;
    349         }
    350     }
    351 #endif // LINUX
    352 
    353 #ifdef TOO_MUCH_INFO
    354     char infobuf[1000];
    355     int i;
    356     for (i = 0; i < 3; i++) {
    357         char *infop = infobuf;
    358         int len, lim = sizeof infobuf;
    359         const char *title;
    360         switch(i) {
    361         case 0:
    362             title = "deleted";
    363             nif = *here;
    364             break;
    365         case 1:
    366             title = "   kept";
    367             nif = kept_ifaddrs;
    368             break;
    369         case 2:
    370             title = "    new";
    371             nif = new_ifaddrs;
    372             break;
    373         default:
    374             abort();
    375         }
    376         for (; nif; nif = nif->next) {
    377             snprintf(infop, lim, "\n%p %s (", nif, nif->name);
    378             len = (int)strlen(infop);
    379             lim -= len;
    380             infop += len;
    381             if (nif->addr.sa.sa_family == AF_INET6) {
    382                 inet_ntop(AF_INET6, &nif->addr.sin6.sin6_addr, infop, lim);
    383             } else if (nif->addr.sa.sa_family == AF_INET) {
    384                 inet_ntop(AF_INET, &nif->addr.sin.sin_addr, infop, lim);
    385             } else if (nif->addr.sa.sa_family == AF_LINK) {
    386                 snprintf(infop, lim, "%02x:%02x:%02x:%02x:%02x:%02x",
    387                          nif->addr.ether_addr.addr[0], nif->addr.ether_addr.addr[1], nif->addr.ether_addr.addr[2],
    388                          nif->addr.ether_addr.addr[3], nif->addr.ether_addr.addr[4], nif->addr.ether_addr.addr[5]);
    389             }
    390             len = (int)strlen(infop);
    391             lim -= len;
    392             infop += len;
    393             if (lim > 1) {
    394                 *infop++ = ')';
    395                 lim--;
    396             }
    397         }
    398         *infop = 0;
    399         INFO(PUB_S_SRP ":" PUB_S_SRP, title, infobuf);
    400     }
    401 #endif
    402 
    403     // Report and free deleted interface addresses...
    404     for (ip = here; *ip; ) {
    405         nif = *ip;
    406         *ip = nif->next;
    407         if (callback != NULL) {
    408             callback(server_state, context, nif->name, &nif->addr, &nif->mask, nif->flags, interface_address_deleted);
    409         }
    410         free(nif);
    411     }
    412 
    413     // Report added interface addresses...
    414     for (nif = new_ifaddrs; nif; nif = nif->next) {
    415         if (callback != NULL) {
    416             callback(server_state, context, nif->name, &nif->addr, &nif->mask, nif->flags, interface_address_added);
    417         }
    418     }
    419 
    420     // Report unchanged interface addresses...
    421     for (nif = kept_ifaddrs; nif; nif = nif->next) {
    422         if (callback != NULL) {
    423             callback(server_state, context, nif->name, &nif->addr, &nif->mask, nif->flags, interface_address_unchanged);
    424         }
    425     }
    426 
    427     // Restore kept interface addresses and append new addresses to the list.
    428     *here = kept_ifaddrs;
    429     for (ip = here; *ip; ip = &(*ip)->next)
    430         ;
    431     *ip = new_ifaddrs;
    432 #ifdef SRP_TEST_SERVER
    433     srp_test_freeifaddrs(server_state, ifaddrs, context);
    434 #else
    435     freeifaddrs(ifaddrs);
    436 #endif
    437     return true;
    438 }
    439 
    440 ssize_t
    441 ioloop_recvmsg(int sock, uint8_t *buffer, size_t buffer_length, int *ifindex, int *hop_limit, addr_t *source,
    442                addr_t *destination)
    443 {
    444     ssize_t rv;
    445     struct msghdr msg;
    446     struct iovec bufp;
    447     char cmsgbuf[128];
    448     struct cmsghdr *cmh;
    449 
    450     bufp.iov_base = buffer;
    451     bufp.iov_len = buffer_length;
    452     msg.msg_iov = &bufp;
    453     msg.msg_iovlen = 1;
    454     msg.msg_name = source;
    455     msg.msg_namelen = sizeof(*source);
    456     msg.msg_control = cmsgbuf;
    457     msg.msg_controllen = sizeof(cmsgbuf);
    458 
    459     rv = recvmsg(sock, &msg, 0);
    460     if (rv < 0) {
    461         return rv;
    462     }
    463 
    464     // For UDP, we use the interface index as part of the validation strategy, so go get
    465     // the interface index.
    466     for (cmh = CMSG_FIRSTHDR(&msg); cmh; cmh = CMSG_NXTHDR(&msg, cmh)) {
    467         if (cmh->cmsg_level == IPPROTO_IPV6 && cmh->cmsg_type == IPV6_PKTINFO &&
    468             cmh->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo)))
    469         {
    470             struct in6_pktinfo pktinfo;
    471 
    472             memcpy(&pktinfo, CMSG_DATA(cmh), sizeof pktinfo);
    473             *ifindex = (int)pktinfo.ipi6_ifindex;
    474 
    475             /* Get the destination address, for use when replying. */
    476             destination->sin6.sin6_family = AF_INET6;
    477             destination->sin6.sin6_port = 0;
    478             destination->sin6.sin6_addr = pktinfo.ipi6_addr;
    479 #ifndef NOT_HAVE_SA_LEN
    480             destination->sin6.sin6_len = sizeof(destination->sin6);
    481 #endif
    482         } else if (cmh->cmsg_level == IPPROTO_IP && cmh->cmsg_type == IP_PKTINFO &&
    483                    cmh->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))) {
    484             struct in_pktinfo pktinfo;
    485 
    486             memcpy(&pktinfo, CMSG_DATA(cmh), sizeof pktinfo);
    487             *ifindex = (int)pktinfo.ipi_ifindex;
    488 
    489             destination->sin.sin_family = AF_INET;
    490             destination->sin.sin_port = 0;
    491             destination->sin.sin_addr = pktinfo.ipi_addr;
    492 #ifndef NOT_HAVE_SA_LEN
    493             destination->sin.sin_len = sizeof(destination->sin);
    494 #endif
    495         } else if (cmh->cmsg_level == IPPROTO_IPV6 && cmh->cmsg_type == IPV6_HOPLIMIT &&
    496                    cmh->cmsg_len == CMSG_LEN(sizeof(int))) {
    497             *hop_limit = *(int *)CMSG_DATA(cmh);
    498         }
    499     }
    500     return rv;
    501 }
    502 
    503 message_t *
    504 ioloop_message_create_(size_t message_size, const char *file, int line)
    505 {
    506     message_t *message;
    507 
    508     // Never should have a message shorter than this.
    509     if (message_size < DNS_HEADER_SIZE || message_size > UINT16_MAX) {
    510         return NULL;
    511     }
    512 
    513     message = (message_t *)malloc(message_size + (sizeof(message_t)) - (sizeof(dns_wire_t)));
    514     if (message) {
    515         memset(message, 0, (sizeof(message_t)) - (sizeof(dns_wire_t)));
    516         RETAIN(message, message);
    517         message->length = (uint16_t)message_size;
    518     }
    519     return message;
    520 }
    521 
    522 // Return continuous time, if provided by O.S., otherwise unadjusted time.
    523 time_t
    524 srp_time(void)
    525 {
    526 #ifdef CLOCK_BOOTTIME
    527     // CLOCK_BOOTTIME is a Linux-specific constant that indicates a monotonic time that includes time asleep
    528     const int clockid = CLOCK_BOOTTIME;
    529 #elif defined(CLOCK_MONOTONIC_RAW)
    530     // On MacOS, CLOCK_MONOTONIC_RAW is a monotonic time that includes time asleep and is not adjusted.
    531     // According to the man page, CLOCK_MONOTONIC on MacOS violates the POSIX spec in that it can be adjusted.
    532     const int clockid = CLOCK_MONOTONIC_RAW;
    533 #else
    534     // On other Posix systems, CLOCK_MONOTONIC should be the right thing, at least according to the POSIX spec.
    535     const int clockid = CLOCK_MONOTONIC;
    536 #endif
    537     struct timespec tm;
    538     clock_gettime(clockid, &tm);
    539 
    540     // We are only accurate to the second.
    541     return tm.tv_sec;
    542 }
    543 
    544 // Return continuous time, if provided by O.S., otherwise unadjusted time, in seconds, with six digits of
    545 // fractional accuracy.
    546 double
    547 srp_fractional_time(void)
    548 {
    549 #ifdef CLOCK_BOOTTIME
    550     // CLOCK_BOOTTIME is a Linux-specific constant that indicates a monotonic time that includes time asleep
    551     const int clockid = CLOCK_BOOTTIME;
    552 #elif defined(CLOCK_MONOTONIC_RAW)
    553     // On MacOS, CLOCK_MONOTONIC_RAW is a monotonic time that includes time asleep and is not adjusted.
    554     // According to the man page, CLOCK_MONOTONIC on MacOS violates the POSIX spec in that it can be adjusted.
    555     const int clockid = CLOCK_MONOTONIC_RAW;
    556 #else
    557     // On other Posix systems, CLOCK_MONOTONIC should be the right thing, at least according to the POSIX spec.
    558     const int clockid = CLOCK_MONOTONIC;
    559 #endif
    560     struct timespec tm;
    561     clock_gettime(clockid, &tm);
    562 
    563     return (double)tm.tv_sec + (double)tm.tv_nsec / 1.0e9;
    564 }
    565 
    566 // Return continuous time in microseconds, if provided by O.S., otherwise unadjusted time.
    567 int64_t
    568 srp_utime(void)
    569 {
    570 #ifdef CLOCK_BOOTTIME
    571     // CLOCK_BOOTTIME is a Linux-specific constant that indicates a monotonic time that includes time asleep
    572     const int clockid = CLOCK_BOOTTIME;
    573 #elif defined(CLOCK_MONOTONIC_RAW)
    574     // On MacOS, CLOCK_MONOTONIC_RAW is a monotonic time that includes time asleep and is not adjusted.
    575     // According to the man page, CLOCK_MONOTONIC on MacOS violates the POSIX spec in that it can be adjusted.
    576     const int clockid = CLOCK_MONOTONIC_RAW;
    577 #else
    578     // On other Posix systems, CLOCK_MONOTONIC should be the right thing, at least according to the POSIX spec.
    579     const int clockid = CLOCK_MONOTONIC;
    580 #endif
    581     struct timespec tm;
    582     clock_gettime(clockid, &tm);
    583 
    584     // We are only accurate to the second.
    585     uint64_t utime = (int64_t)tm.tv_sec * 1000 * 1000 + tm.tv_nsec / 1000;
    586     return utime;
    587 }
    588 
    589 int
    590 get_num_fds(void)
    591 {
    592     int num = 0;
    593     DIR *dirfd = opendir("/dev/fd");
    594     if (dirfd == NULL) {
    595         if (errno == EMFILE) {
    596             FAULT("per-process open file limit reached.");
    597             return -1;
    598         } else if (errno == ENFILE) {
    599             FAULT("per-system open file limit reached.");
    600             return -1;
    601         } else {
    602             ERROR("errno %d " PUB_S_SRP, errno, strerror(errno));
    603             return 0;
    604         }
    605     }
    606     while (readdir(dirfd) != NULL) {
    607         num++;
    608     }
    609     closedir(dirfd);
    610     return num;
    611 }
    612 
    613 #ifdef MALLOC_DEBUG_LOGGING
    614 #undef malloc
    615 #undef calloc
    616 #undef strdup
    617 #undef free
    618 
    619 void *
    620 debug_malloc(size_t len, const char *file, int line)
    621 {
    622     void *ret = malloc(len);
    623     INFO("%p: malloc(%zu) at " PUB_S_SRP ":%d", ret, len, file, line);
    624     return ret;
    625 }
    626 
    627 void *
    628 debug_calloc(size_t count, size_t len, const char *file, int line)
    629 {
    630     void *ret = calloc(count, len);
    631     INFO("%p: calloc(%zu, %zu) at " PUB_S_SRP ":%d", ret, count, len, file, line);
    632     return ret;
    633 }
    634 
    635 char *
    636 debug_strdup(const char *s, const char *file, int line)
    637 {
    638     char *ret = strdup(s);
    639     INFO("%p: strdup(%p) at " PUB_S_SRP ":%d", ret, s, file, line);
    640     return ret;
    641 }
    642 
    643 void
    644 debug_free(void *p, const char *file, int line)
    645 {
    646     INFO("%p: free() at " PUB_S_SRP ":%d", p, file, line);
    647     free(p);
    648 }
    649 #endif
    650 
    651 // Local Variables:
    652 // mode: C
    653 // tab-width: 4
    654 // c-file-style: "bsd"
    655 // c-basic-offset: 4
    656 // fill-column: 108
    657 // indent-tabs-mode: nil
    658 // End:
    659