1 /**
2 * Copyright (C) Mellanox Technologies Ltd. 2001-2012.  ALL RIGHTS RESERVED.
3 * Copyright (c) UT-Battelle, LLC. 2014-2019. ALL RIGHTS RESERVED.
4 * Copyright (C) ARM Ltd. 2016-2017.  ALL RIGHTS RESERVED.
5 *
6 * See file LICENSE for terms.
7 */
8 
9 #ifdef HAVE_CONFIG_H
10 #  include "config.h"
11 #endif
12 
13 
14 #include <ucs/algorithm/crc.h>
15 #include <ucs/sys/checker.h>
16 #include <ucs/sys/string.h>
17 #include <ucs/sys/sys.h>
18 #include <ucs/debug/log.h>
19 #include <ucs/time/time.h>
20 #include <ucs/type/init_once.h>
21 #include <ucm/util/sys.h>
22 
23 #include <unistd.h>
24 #include <sys/stat.h>
25 #include <sys/ioctl.h>
26 #include <sys/shm.h>
27 #include <sys/mman.h>
28 #include <sys/types.h>
29 #include <net/if.h>
30 #include <dirent.h>
31 #include <sched.h>
32 #include <ctype.h>
33 #ifdef HAVE_SYS_THR_H
34 #include <sys/thr.h>
35 #endif
36 
37 #if HAVE_SYS_CAPABILITY_H
38 #  include <sys/capability.h>
39 #endif
40 
41 /* Default huge page size is 2 MBytes */
42 #define UCS_DEFAULT_MEM_FREE       640000
43 #define UCS_PROCESS_SMAPS_FILE     "/proc/self/smaps"
44 #define UCS_PROCESS_NS_DIR         "/proc/self/ns"
45 #define UCS_PROCESS_BOOTID_FILE    "/proc/sys/kernel/random/boot_id"
46 #define UCS_PROCESS_BOOTID_FMT     "%x-%4hx-%4hx-%4hx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx"
47 #define UCS_PROCESS_NS_FIRST       0xF0000000U
48 #define UCS_PROCESS_NS_NET_DFLT    0xF0000080U
49 
50 
51 struct {
52     const char  *name;
53     ucs_sys_ns_t dflt;
54 } static ucs_sys_namespace_info[] = {
55     [UCS_SYS_NS_TYPE_IPC]  = {.name = "ipc",  .dflt = UCS_PROCESS_NS_FIRST - 1},
56     [UCS_SYS_NS_TYPE_MNT]  = {.name = "mnt",  .dflt = UCS_PROCESS_NS_FIRST - 0},
57     [UCS_SYS_NS_TYPE_NET]  = {.name = "net",  .dflt = UCS_PROCESS_NS_NET_DFLT},
58     [UCS_SYS_NS_TYPE_PID]  = {.name = "pid",  .dflt = UCS_PROCESS_NS_FIRST - 4},
59     [UCS_SYS_NS_TYPE_USER] = {.name = "user", .dflt = UCS_PROCESS_NS_FIRST - 3},
60     [UCS_SYS_NS_TYPE_UTS]  = {.name = "uts",  .dflt = UCS_PROCESS_NS_FIRST - 2}
61 };
62 
63 typedef struct {
64     void                     *ctx;
65     ucs_sys_enum_threads_cb_t cb;
66 } ucs_sys_enum_threads_t;
67 
68 
69 static const char *ucs_pagemap_file = "/proc/self/pagemap";
70 
71 
ucs_get_tmpdir()72 const char *ucs_get_tmpdir()
73 {
74     char *env_tmpdir;
75 
76     env_tmpdir = getenv("TMPDIR");
77     if (env_tmpdir) {
78         return env_tmpdir;
79     } else {
80         return "/tmp/";
81     }
82 }
83 
ucs_get_host_name()84 const char *ucs_get_host_name()
85 {
86     static char hostname[HOST_NAME_MAX] = {0};
87 
88     if (*hostname == 0) {
89         gethostname(hostname, sizeof(hostname));
90         strtok(hostname, ".");
91     }
92     return hostname;
93 }
94 
ucs_get_user_name()95 const char *ucs_get_user_name()
96 {
97     static char username[256] = {0};
98 
99     if (*username == 0) {
100         getlogin_r(username, sizeof(username));
101     }
102     return username;
103 }
104 
ucs_expand_path(const char * path,char * fullpath,size_t max)105 void ucs_expand_path(const char *path, char *fullpath, size_t max)
106 {
107     char cwd[1024] = {0};
108 
109     if (path[0] == '/') {
110             strncpy(fullpath, path, max);
111     } else if (getcwd(cwd, sizeof(cwd) - 1) != NULL) {
112         snprintf(fullpath, max, "%s/%s", cwd, path);
113     } else {
114         ucs_warn("failed to expand path '%s' (%m), using original path", path);
115         strncpy(fullpath, path, max);
116     }
117 }
118 
ucs_get_exe()119 const char *ucs_get_exe()
120 {
121     static char exe[1024];
122     int ret;
123 
124     ret = readlink("/proc/self/exe", exe, sizeof(exe) - 1);
125     if (ret < 0) {
126         exe[0] = '\0';
127     } else {
128         exe[ret] = '\0';
129     }
130 
131     return exe;
132 }
133 
ucs_file_checksum(const char * filename)134 uint32_t ucs_file_checksum(const char *filename)
135 {
136     char buffer[1024];
137     ssize_t nread;
138     int fd;
139     uint32_t crc;
140 
141     fd = open(filename, O_RDONLY);
142     if (fd < 0) {
143         return 0;
144     }
145 
146     crc = 0;
147     do {
148         nread = read(fd, buffer, sizeof(buffer));
149         if (nread > 0) {
150             crc = ucs_crc32(crc, buffer, nread);
151         }
152     } while (nread == sizeof(buffer));
153     close(fd);
154 
155     return crc;
156 }
157 
ucs_get_mac_address()158 static uint64_t ucs_get_mac_address()
159 {
160     static uint64_t mac_address = 0;
161     struct ifreq ifr, *it, *end;
162     struct ifconf ifc;
163     char buf[1024];
164     int sock;
165 
166     if (mac_address == 0) {
167         sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
168         if (sock == -1) {
169             ucs_error("failed to create socket: %m");
170             return 0;
171         }
172 
173         ifc.ifc_len = sizeof(buf);
174         ifc.ifc_buf = buf;
175         if (ioctl(sock, SIOCGIFCONF, &ifc) == -1) {
176             ucs_error("ioctl(SIOCGIFCONF) failed: %m");
177             close(sock);
178             return 0;
179         }
180 
181         it = ifc.ifc_req;
182         end = it + (ifc.ifc_len / sizeof *it);
183         for (it = ifc.ifc_req; it != end; ++it) {
184             strcpy(ifr.ifr_name, it->ifr_name);
185             if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) {
186                 ucs_error("ioctl(SIOCGIFFLAGS) failed: %m");
187                 close(sock);
188                 return 0;
189             }
190 
191             if (!(ifr.ifr_flags & IFF_LOOPBACK)) {
192                 if (ioctl(sock, SIOCGIFHWADDR, &ifr) != 0) {
193                     ucs_error("ioctl(SIOCGIFHWADDR) failed: %m");
194                     close(sock);
195                     return 0;
196                 }
197 
198                 memcpy(&mac_address, ifr.ifr_hwaddr.sa_data, 6);
199                 break;
200             }
201         }
202 
203         close(sock);
204         ucs_trace("MAC address is 0x%012"PRIX64, mac_address);
205     }
206 
207     return mac_address;
208 }
209 
__sumup_host_name(unsigned prime_index)210 static uint64_t __sumup_host_name(unsigned prime_index)
211 {
212     uint64_t sum, n;
213     const char *p;
214     unsigned i;
215 
216     sum = 0;
217     i = prime_index;
218     p = ucs_get_host_name();
219     while (*p != '\0') {
220         n = 0;
221         memcpy(&n, p, strnlen(p, sizeof(n)));
222         sum += ucs_get_prime(i) * n;
223         ++i;
224         p += ucs_min(sizeof(n), strlen(p));
225     }
226     return sum;
227 }
228 
ucs_machine_guid()229 uint64_t ucs_machine_guid()
230 {
231     return ucs_get_prime(0) * ucs_get_mac_address() +
232            __sumup_host_name(1);
233 }
234 
235 /*
236  * If a certain system constant (name) is undefined on the underlying system the
237  * sysconf routine returns -1.  ucs_sysconf return the negative value
238  * a user and the user is responsible to define default value or abort.
239  *
240  * If an error occurs sysconf modified errno and ucs_sysconf aborts.
241  *
242  * Otherwise, a non-negative values is returned.
243  */
ucs_sysconf(int name)244 static long ucs_sysconf(int name)
245 {
246     long rc;
247     errno = 0;
248 
249     rc = sysconf(name);
250     ucs_assert_always(errno == 0);
251 
252     return rc;
253 }
254 
ucs_get_first_cpu()255 int ucs_get_first_cpu()
256 {
257     int first_cpu, total_cpus, ret;
258     ucs_sys_cpuset_t mask;
259 
260     ret = ucs_sysconf(_SC_NPROCESSORS_CONF);
261     if (ret < 0) {
262         ucs_error("failed to get local cpu count: %m");
263         return ret;
264     }
265     total_cpus = ret;
266 
267     CPU_ZERO(&mask);
268     ret = ucs_sys_getaffinity(&mask);
269     if (ret < 0) {
270         ucs_error("failed to get process affinity: %m");
271         return ret;
272     }
273 
274     for (first_cpu = 0; first_cpu < total_cpus; ++first_cpu) {
275         if (CPU_ISSET(first_cpu, &mask)) {
276             return first_cpu;
277         }
278     }
279 
280     return total_cpus;
281 }
282 
ucs_generate_uuid(uint64_t seed)283 uint64_t ucs_generate_uuid(uint64_t seed)
284 {
285     struct timeval tv;
286 
287     gettimeofday(&tv, NULL);
288     return seed +
289            ucs_get_prime(0) * ucs_get_tid() +
290            ucs_get_prime(1) * ucs_get_time() +
291            ucs_get_prime(2) * ucs_get_mac_address() +
292            ucs_get_prime(3) * tv.tv_sec +
293            ucs_get_prime(4) * tv.tv_usec +
294            __sumup_host_name(5);
295 }
296 
297 ucs_status_t
ucs_open_output_stream(const char * config_str,ucs_log_level_t err_log_level,FILE ** p_fstream,int * p_need_close,const char ** p_next_token,char ** p_filename)298 ucs_open_output_stream(const char *config_str, ucs_log_level_t err_log_level,
299                        FILE **p_fstream, int *p_need_close,
300                        const char **p_next_token, char **p_filename)
301 {
302     FILE *output_stream;
303     char filename[256];
304     char *template;
305     const char *p;
306     size_t len;
307 
308     *p_next_token   = config_str;
309     if (p_filename != NULL) {
310         *p_filename = NULL;
311     }
312 
313     len = strcspn(config_str, ":");
314     if (!strncmp(config_str, "stdout", len)) {
315         *p_fstream    = stdout;
316         *p_need_close = 0;
317         *p_next_token = config_str + len;
318     } else if (!strncmp(config_str, "stderr", len)) {
319         *p_fstream    = stderr;
320         *p_need_close = 0;
321         *p_next_token = config_str + len;
322     } else {
323         if (!strncmp(config_str, "file:", 5)) {
324             p = config_str + 5;
325         } else {
326             p = config_str;
327         }
328 
329         len = strcspn(p, ":");
330         template = strndup(p, len);
331         ucs_fill_filename_template(template, filename, sizeof(filename));
332         free(template);
333 
334         output_stream = fopen(filename, "w");
335         if (output_stream == NULL) {
336             ucs_log(err_log_level, "failed to open '%s' for writing: %m",
337                     filename);
338             return UCS_ERR_IO_ERROR;
339         }
340 
341         if (p_filename != NULL) {
342             *p_filename = ucs_strdup(filename, "filename");
343             if (*p_filename == NULL) {
344                 ucs_log(err_log_level, "failed to allocate filename for '%s'",
345                         filename);
346                 fclose(output_stream);
347                 return UCS_ERR_NO_MEMORY;
348             }
349         }
350 
351         *p_fstream    = output_stream;
352         *p_need_close = 1;
353         *p_next_token = p + len;
354     }
355 
356     return UCS_OK;
357 }
358 
ucs_read_file_vararg(char * buffer,size_t max,int silent,const char * filename_fmt,va_list ap)359 static ssize_t ucs_read_file_vararg(char *buffer, size_t max, int silent,
360                                     const char *filename_fmt, va_list ap)
361 {
362     char filename[MAXPATHLEN];
363     ssize_t read_bytes;
364     int fd;
365 
366     vsnprintf(filename, MAXPATHLEN, filename_fmt, ap);
367 
368     fd = open(filename, O_RDONLY);
369     if (fd < 0) {
370         if (!silent) {
371             ucs_error("failed to open %s: %m", filename);
372         }
373         read_bytes = -1;
374         goto out;
375     }
376 
377     read_bytes = read(fd, buffer, max - 1);
378     if (read_bytes < 0) {
379         if (!silent) {
380             ucs_error("failed to read from %s: %m", filename);
381         }
382         goto out_close;
383     }
384 
385     if (read_bytes < max) {
386         buffer[read_bytes] = '\0';
387     }
388 
389 out_close:
390     close(fd);
391 out:
392     return read_bytes;
393 }
394 
ucs_read_file(char * buffer,size_t max,int silent,const char * filename_fmt,...)395 ssize_t ucs_read_file(char *buffer, size_t max, int silent,
396                       const char *filename_fmt, ...)
397 {
398     ssize_t read_bytes;
399     va_list ap;
400 
401     va_start(ap, filename_fmt);
402     read_bytes = ucs_read_file_vararg(buffer, max, silent, filename_fmt, ap);
403     va_end(ap);
404 
405     return read_bytes;
406 }
407 
ucs_read_file_number(long * value,int silent,const char * filename_fmt,...)408 ucs_status_t ucs_read_file_number(long *value, int silent,
409                                   const char *filename_fmt, ...)
410 {
411     char buffer[64], *tail;
412     ssize_t read_bytes;
413     va_list ap;
414     long n;
415 
416     va_start(ap, filename_fmt);
417     read_bytes = ucs_read_file_vararg(buffer, sizeof(buffer) - 1, silent,
418                                       filename_fmt, ap);
419     va_end(ap);
420 
421     if (read_bytes < 0) {
422         /* read error */
423         return UCS_ERR_IO_ERROR;
424     }
425 
426     n = strtol(buffer, &tail, 0);
427     if ((*tail != '\0') && !isspace(*tail)) {
428         /* parse error */
429         return UCS_ERR_INVALID_PARAM;
430     }
431 
432     *value = n;
433     return UCS_OK;
434 }
435 
ucs_read_file_str(char * buffer,size_t max,int silent,const char * filename_fmt,...)436 ssize_t ucs_read_file_str(char *buffer, size_t max, int silent,
437                           const char *filename_fmt, ...)
438 {
439     size_t max_read = ucs_max(max, 1) - 1;
440     ssize_t read_bytes;
441     va_list ap;
442 
443     va_start(ap, filename_fmt);
444     read_bytes = ucs_read_file_vararg(buffer, max_read, silent, filename_fmt, ap);
445     va_end(ap);
446 
447     if ((read_bytes >= 0) && (max > 0)) {
448         buffer[read_bytes] = '\0';
449     }
450 
451     return read_bytes;
452 }
453 
ucs_get_page_size()454 size_t ucs_get_page_size()
455 {
456     static long page_size = 0;
457 
458     if (page_size == 0) {
459         page_size = ucs_sysconf(_SC_PAGESIZE);
460         if (page_size < 0) {
461             page_size = 4096;
462             ucs_debug("_SC_PAGESIZE is undefined, setting default value to %ld",
463                       page_size);
464         }
465     }
466     return page_size;
467 }
468 
ucs_get_mem_page_size(void * address,size_t size,size_t * min_page_size_p,size_t * max_page_size_p)469 void ucs_get_mem_page_size(void *address, size_t size, size_t *min_page_size_p,
470                            size_t *max_page_size_p)
471 {
472     int found = 0;
473     unsigned long start, end;
474     unsigned long page_size_kb;
475     size_t page_size;
476     char buf[1024];
477     FILE *file;
478     int n;
479 
480     file = fopen(UCS_PROCESS_SMAPS_FILE, "r");
481     if (!file) {
482         goto out;
483     }
484 
485     while (fgets(buf, sizeof(buf), file) != NULL) {
486         n = sscanf(buf, "%lx-%lx", &start, &end);
487         if (n != 2) {
488             continue;
489         }
490 
491         if (start > (uintptr_t)address + size) {
492             /* the scanned range is after memory range of interest - stop */
493             break;
494         }
495         if (end <= (uintptr_t)address) {
496             /* the scanned range is still before the memory range of interest */
497             continue;
498         }
499 
500         while (fgets(buf, sizeof(buf), file) != NULL) {
501             n = sscanf(buf, "KernelPageSize: %lu kB", &page_size_kb);
502             if (n < 1) {
503                 continue;
504             }
505 
506             page_size = page_size_kb * UCS_KBYTE;
507             if (found) {
508                 *min_page_size_p = ucs_min(*min_page_size_p, page_size);
509                 *max_page_size_p = ucs_max(*max_page_size_p, page_size);
510             } else {
511                 found            = 1;
512                 *min_page_size_p = page_size;
513                 *max_page_size_p = page_size;
514             }
515             break;
516         }
517     }
518 
519     fclose(file);
520 
521 out:
522     if (!found) {
523         *min_page_size_p = *max_page_size_p = ucs_get_page_size();
524     }
525 }
526 
ucs_get_meminfo_entry(const char * pattern)527 static ssize_t ucs_get_meminfo_entry(const char* pattern)
528 {
529     char buf[256];
530     char final_pattern[80];
531     int val = 0;
532     ssize_t val_b = -1;
533     FILE *f;
534 
535     f = fopen("/proc/meminfo", "r");
536     if (f != NULL) {
537         snprintf(final_pattern, sizeof(final_pattern), "%s: %s", pattern,
538                  "%d kB");
539         while (fgets(buf, sizeof(buf), f)) {
540             if (sscanf(buf, final_pattern, &val) == 1) {
541                 val_b = val * 1024ull;
542                 break;
543             }
544         }
545         fclose(f);
546     }
547 
548     return val_b;
549 }
550 
ucs_get_memfree_size()551 size_t ucs_get_memfree_size()
552 {
553     ssize_t mem_free;
554 
555     mem_free = ucs_get_meminfo_entry("MemFree");
556     if (mem_free == -1) {
557         mem_free = UCS_DEFAULT_MEM_FREE;
558         ucs_info("cannot determine free mem size, using default: %zu",
559                   mem_free);
560     }
561 
562     return mem_free;
563 }
564 
ucs_get_huge_page_size()565 ssize_t ucs_get_huge_page_size()
566 {
567     static ssize_t huge_page_size = 0;
568 
569     /* Cache the huge page size value */
570     if (huge_page_size == 0) {
571         huge_page_size = ucs_get_meminfo_entry("Hugepagesize");
572         if (huge_page_size == -1) {
573             ucs_debug("huge pages are not supported on the system");
574         } else {
575             ucs_trace("detected huge page size: %zu", huge_page_size);
576         }
577     }
578 
579     return huge_page_size;
580 }
581 
ucs_get_phys_mem_size()582 size_t ucs_get_phys_mem_size()
583 {
584     static size_t phys_mem_size = 0;
585     long phys_pages;
586 
587     if (phys_mem_size == 0) {
588         phys_pages = ucs_sysconf(_SC_PHYS_PAGES);
589         if (phys_pages < 0) {
590             ucs_debug("_SC_PHYS_PAGES is undefined, setting default value to %ld",
591                       SIZE_MAX);
592             phys_mem_size = SIZE_MAX;
593         } else {
594             phys_mem_size = phys_pages * ucs_get_page_size();
595         }
596     }
597     return phys_mem_size;
598 }
599 
600 #define UCS_SYS_THP_ENABLED_FILE "/sys/kernel/mm/transparent_hugepage/enabled"
ucs_is_thp_enabled()601 int ucs_is_thp_enabled()
602 {
603     char buf[256];
604     int rc;
605 
606     rc = ucs_read_file(buf, sizeof(buf) - 1, 1, UCS_SYS_THP_ENABLED_FILE);
607     if (rc < 0) {
608         ucs_debug("failed to read %s:%m", UCS_SYS_THP_ENABLED_FILE);
609         return 0;
610     }
611 
612     buf[rc] = 0;
613     return (strstr(buf, "[never]") == NULL);
614 }
615 
616 #define UCS_PROC_SYS_SHMMAX_FILE "/proc/sys/kernel/shmmax"
ucs_get_shmmax()617 size_t ucs_get_shmmax()
618 {
619     ucs_status_t status;
620     long size;
621 
622     status = ucs_read_file_number(&size, 0, UCS_PROC_SYS_SHMMAX_FILE);
623     if (status != UCS_OK) {
624         ucs_warn("failed to read %s:%m", UCS_PROC_SYS_SHMMAX_FILE);
625         return 0;
626     }
627 
628     return size;
629 }
630 
ucs_sysv_shmget_error_check_ENOSPC(size_t alloc_size,const struct shminfo * ipc_info,char * buf,size_t max)631 static void ucs_sysv_shmget_error_check_ENOSPC(size_t alloc_size,
632                                                const struct shminfo *ipc_info,
633                                                char *buf, size_t max)
634 {
635     unsigned long new_used_ids;
636     unsigned long new_shm_tot;
637     struct shm_info shm_info;
638     char *p, *endp;
639     int ret;
640 
641     p    = buf;
642     endp = p + max;
643 
644     ret = shmctl(0, SHM_INFO, (struct shmid_ds *)&shm_info);
645     if (ret >= 0) {
646         return;
647     }
648 
649     new_used_ids = shm_info.used_ids;
650     if (new_used_ids > ipc_info->shmmni) {
651         snprintf(p, endp - p,
652                  ", total number of segments in the system (%lu) would exceed the"
653                  " limit in /proc/sys/kernel/shmmni (=%lu)",
654                  new_used_ids, ipc_info->shmmni);
655         p += strlen(p);
656     }
657 
658     new_shm_tot = shm_info.shm_tot +
659                   (alloc_size + ucs_get_page_size() - 1) / ucs_get_page_size();
660     if (new_shm_tot > ipc_info->shmall) {
661         snprintf(p, endp - p,
662                  ", total shared memory pages in the system (%lu) would exceed the"
663                  " limit in /proc/sys/kernel/shmall (=%lu)",
664                  new_shm_tot, ipc_info->shmall);
665     }
666 }
667 
ucs_sys_get_proc_cap(uint32_t * effective)668 ucs_status_t ucs_sys_get_proc_cap(uint32_t *effective)
669 {
670 #if HAVE_SYS_CAPABILITY_H
671     cap_user_header_t hdr = ucs_alloca(sizeof(*hdr));
672     cap_user_data_t data  = ucs_alloca(sizeof(*data) * _LINUX_CAPABILITY_U32S_3);
673     int ret;
674 
675     hdr->pid     = 0; /* current thread */
676     hdr->version = _LINUX_CAPABILITY_VERSION_3;
677     ret = capget(hdr, data);
678     if (ret) {
679         ucs_debug("capget(pid=%d version=0x%x) failed: %m", hdr->pid,
680                   hdr->version);
681         return UCS_ERR_IO_ERROR;
682 
683     }
684 
685     *effective = data->effective;
686     return UCS_OK;
687 #else
688     return UCS_ERR_UNSUPPORTED;
689 #endif
690 }
691 
ucs_sysv_shmget_error_check_EPERM(int flags,char * buf,size_t max)692 static void ucs_sysv_shmget_error_check_EPERM(int flags, char *buf, size_t max)
693 {
694 #if HAVE_SYS_CAPABILITY_H
695     ucs_status_t status;
696     uint32_t ecap;
697 
698     UCS_STATIC_ASSERT(CAP_IPC_LOCK < 32);
699     status = ucs_sys_get_proc_cap(&ecap);
700     if ((status == UCS_OK) && !(ecap & UCS_BIT(CAP_IPC_LOCK))) {
701         /* detected missing CAP_IPC_LOCK */
702         snprintf(buf, max, ", CAP_IPC_LOCK privilege is needed for SHM_HUGETLB");
703         return;
704     }
705 #endif
706 
707     snprintf(buf, max,
708              ", please check for CAP_IPC_LOCK privilege for using SHM_HUGETLB");
709 }
710 
ucs_sysv_shmget_format_error(size_t alloc_size,int flags,const char * alloc_name,int sys_errno,char * buf,size_t max)711 static void ucs_sysv_shmget_format_error(size_t alloc_size, int flags,
712                                          const char *alloc_name, int sys_errno,
713                                          char *buf, size_t max)
714 {
715     struct shminfo ipc_info;
716     char *p, *endp, *errp;
717     int ret;
718 
719     buf[0] = '\0';
720     p      = buf;
721     endp   = p + max;
722 
723     snprintf(p, endp - p, "shmget(size=%zu flags=0x%x) for %s failed: %s",
724              alloc_size, flags, alloc_name, strerror(sys_errno));
725     p   += strlen(p);
726     errp = p; /* save current string pointer to detect if anything was added */
727 
728     ret = shmctl(0, IPC_INFO, (struct shmid_ds *)&ipc_info);
729     if (ret >= 0) {
730         if ((sys_errno == EINVAL) && (alloc_size > ipc_info.shmmax)) {
731             snprintf(p, endp - p,
732                      ", allocation size exceeds /proc/sys/kernel/shmmax (=%zu)",
733                      ipc_info.shmmax);
734             p += strlen(p);
735         }
736 
737         if (sys_errno == ENOSPC) {
738             ucs_sysv_shmget_error_check_ENOSPC(alloc_size, &ipc_info, p, endp - p);
739             p += strlen(p);
740         }
741     }
742 
743     if (sys_errno == EPERM) {
744         ucs_sysv_shmget_error_check_EPERM(flags, p, endp - p);
745         p += strlen(p);
746     }
747 
748     /* default error message if no useful information was added to the string */
749     if (p == errp) {
750         snprintf(p, endp - p, ", please check shared memory limits by 'ipcs -l'");
751     }
752 }
753 
ucs_sysv_alloc(size_t * size,size_t max_size,void ** address_p,int flags,const char * alloc_name,int * shmid)754 ucs_status_t ucs_sysv_alloc(size_t *size, size_t max_size, void **address_p,
755                             int flags, const char *alloc_name, int *shmid)
756 {
757     char error_string[256];
758 #ifdef SHM_HUGETLB
759     ssize_t huge_page_size;
760 #endif
761     size_t alloc_size;
762     int sys_errno;
763     void *ptr;
764     int ret;
765 
766 #ifdef SHM_HUGETLB
767     if (flags & SHM_HUGETLB) {
768         huge_page_size = ucs_get_huge_page_size();
769         if (huge_page_size <= 0) {
770             ucs_debug("huge pages are not supported on the system");
771             return UCS_ERR_NO_MEMORY; /* Huge pages not supported */
772         }
773 
774         alloc_size = ucs_align_up(*size, huge_page_size);
775     } else
776 #endif
777     {
778         alloc_size = ucs_align_up(*size, ucs_get_page_size());
779     }
780 
781     if (alloc_size >= max_size) {
782         return UCS_ERR_EXCEEDS_LIMIT;
783     }
784 
785     flags |= IPC_CREAT | SHM_R | SHM_W;
786     *shmid = shmget(IPC_PRIVATE, alloc_size, flags);
787     if (*shmid < 0) {
788         sys_errno = errno;
789         ucs_sysv_shmget_format_error(alloc_size, flags, alloc_name, sys_errno,
790                                      error_string, sizeof(error_string));
791         switch (sys_errno) {
792         case ENOMEM:
793         case EPERM:
794 #ifdef SHM_HUGETLB
795             if (!(flags & SHM_HUGETLB))
796 #endif
797 	    {
798                 ucs_error("%s", error_string);
799             }
800             return UCS_ERR_NO_MEMORY;
801         case ENOSPC:
802         case EINVAL:
803             ucs_error("%s", error_string);
804             return UCS_ERR_NO_MEMORY;
805         default:
806             ucs_error("%s", error_string);
807             return UCS_ERR_SHMEM_SEGMENT;
808         }
809     }
810 
811     /* Attach segment */
812     if (*address_p) {
813         ptr = shmat(*shmid, *address_p, SHM_REMAP);
814     } else {
815         ptr = shmat(*shmid, NULL, 0);
816     }
817 
818     /* Remove segment, the attachment keeps a reference to the mapping */
819     /* FIXME having additional attaches to a removed segment is not portable
820     * behavior */
821     ret = shmctl(*shmid, IPC_RMID, NULL);
822     if (ret != 0) {
823         ucs_warn("shmctl(IPC_RMID, shmid=%d) returned %d: %m", *shmid, ret);
824     }
825 
826     /* Check if attachment was successful */
827     if (ptr == (void*)-1) {
828         if (errno == ENOMEM) {
829             return UCS_ERR_NO_MEMORY;
830         } else if (RUNNING_ON_VALGRIND && (errno == EINVAL)) {
831             return UCS_ERR_NO_MEMORY;
832         } else {
833             ucs_error("shmat(shmid=%d) returned unexpected error: %m", *shmid);
834             return UCS_ERR_SHMEM_SEGMENT;
835         }
836     }
837 
838     ucs_memtrack_allocated(ptr, alloc_size UCS_MEMTRACK_VAL);
839     *address_p = ptr;
840     *size      = alloc_size;
841     return UCS_OK;
842 }
843 
ucs_sysv_free(void * address)844 ucs_status_t ucs_sysv_free(void *address)
845 {
846     int ret;
847 
848     ucs_memtrack_releasing(address);
849     ret = shmdt(address);
850     if (ret) {
851         ucs_warn("Unable to detach shared memory segment at %p: %m", address);
852         return UCS_ERR_INVALID_PARAM;
853     }
854 
855     return UCS_OK;
856 }
857 
ucs_mmap_alloc(size_t * size,void ** address_p,int flags UCS_MEMTRACK_ARG)858 ucs_status_t ucs_mmap_alloc(size_t *size, void **address_p,
859                             int flags UCS_MEMTRACK_ARG)
860 {
861     size_t alloc_length;
862     void *addr;
863 
864     alloc_length = ucs_align_up_pow2(*size, ucs_get_page_size());
865 
866     addr = ucs_mmap(*address_p, alloc_length, PROT_READ | PROT_WRITE,
867                     MAP_PRIVATE | MAP_ANON | flags, -1, 0 UCS_MEMTRACK_VAL);
868     if (addr == MAP_FAILED) {
869         return UCS_ERR_NO_MEMORY;
870     }
871 
872     *size      = alloc_length;
873     *address_p = addr;
874     return UCS_OK;
875 }
876 
ucs_mmap_free(void * address,size_t length)877 ucs_status_t ucs_mmap_free(void *address, size_t length)
878 {
879     int ret;
880     size_t alloc_length;
881 
882     alloc_length = ucs_align_up_pow2(length, ucs_get_page_size());
883 
884     ret = ucs_munmap(address, alloc_length);
885     if (ret != 0) {
886         ucs_warn("munmap(address=%p, length=%zu) failed: %m", address, length);
887         return UCS_ERR_INVALID_PARAM;
888     }
889     return UCS_OK;
890 }
891 
892 typedef struct {
893     unsigned long start;
894     unsigned long end;
895     int           prot;
896     int           found;
897 } ucs_get_mem_prot_ctx_t;
898 
ucs_get_mem_prot_cb(void * arg,void * addr,size_t length,int prot,const char * path)899 static int ucs_get_mem_prot_cb(void *arg, void *addr, size_t length, int prot,
900                                const char *path)
901 {
902     ucs_get_mem_prot_ctx_t *ctx = arg;
903     unsigned long seg_start = (uintptr_t)addr;
904     unsigned long seg_end   = (uintptr_t)addr + length;
905 
906     if (ctx->start < seg_start) {
907         ucs_trace("address 0x%lx is before next mapping 0x%lx..0x%lx", ctx->start,
908                   seg_start, seg_end);
909         return 1;
910     } else if (ctx->start < seg_end) {
911         ucs_trace("range 0x%lx..0x%lx overlaps with mapping 0x%lx..0x%lx prot 0x%x",
912                   ctx->start, ctx->end, seg_start, seg_end, prot);
913 
914         if (!ctx->found) {
915             /* first segment sets protection flags */
916             ctx->prot  = prot;
917             ctx->found = 1;
918         } else {
919             /* subsequent segments update protection flags */
920             ctx->prot &= prot;
921         }
922 
923         if (ctx->end <= seg_end) {
924             /* finished going over entire memory region */
925             return 1;
926         }
927 
928         /* continue from the end of current segment */
929         ctx->start = seg_end;
930     }
931     return 0;
932 }
933 
ucs_get_mem_prot(unsigned long start,unsigned long end)934 int ucs_get_mem_prot(unsigned long start, unsigned long end)
935 {
936     ucs_get_mem_prot_ctx_t ctx = { start, end, PROT_NONE, 0 };
937     ucm_parse_proc_self_maps(ucs_get_mem_prot_cb, &ctx);
938     return ctx.prot;
939 }
940 
ucs_get_process_cmdline()941 const char* ucs_get_process_cmdline()
942 {
943     static char cmdline[1024] = {0};
944     static int initialized = 0;
945     ssize_t len;
946     int i;
947 
948     if (!initialized) {
949         len = ucs_read_file(cmdline, sizeof(cmdline), 1, "/proc/self/cmdline");
950         for (i = 0; i < len; ++i) {
951             if (cmdline[i] == '\0') {
952                 cmdline[i] = ' ';
953             }
954         }
955         initialized = 1;
956     }
957     return cmdline;
958 }
959 
960 static ucs_status_t
ucs_sys_enum_pfn_internal(int pagemap_fd,unsigned start_page,uint64_t * data,uintptr_t address,unsigned page_count,ucs_sys_enum_pfn_cb_t cb,void * ctx)961 ucs_sys_enum_pfn_internal(int pagemap_fd, unsigned start_page, uint64_t *data,
962                           uintptr_t address, unsigned page_count,
963                           ucs_sys_enum_pfn_cb_t cb, void *ctx)
964 {
965     off_t offset;
966     ssize_t ret;
967     size_t len;
968     unsigned i;
969 
970     offset = ((address / ucs_get_page_size()) + start_page) * sizeof(*data);
971     len    = page_count * sizeof(*data);
972     ret    = pread(pagemap_fd, data, len, offset);
973     if (ret < 0) {
974         ucs_warn("pread(file=%s offset=%zu) failed: %m", ucs_pagemap_file, offset);
975         return UCS_ERR_IO_ERROR;
976     }
977 
978     for (i = 0; i < ret / sizeof(*data); i++) {
979         if (!(data[i] & UCS_BIT(63))) {
980             ucs_trace("address 0x%lx not present",
981                       address + (ucm_get_page_size() * (i + start_page)));
982             return UCS_ERR_IO_ERROR;
983         }
984 
985         cb(i + start_page, data[i] & UCS_MASK(55), ctx);
986     }
987 
988     return UCS_OK;
989 }
990 
ucs_sys_enum_pfn(uintptr_t address,unsigned page_count,ucs_sys_enum_pfn_cb_t cb,void * ctx)991 ucs_status_t ucs_sys_enum_pfn(uintptr_t address, unsigned page_count,
992                               ucs_sys_enum_pfn_cb_t cb, void *ctx)
993 {
994     /* by default use 1K buffer on stack */
995     const int UCS_SYS_ENUM_PFN_ELEM_CNT = ucs_min(128, UCS_ALLOCA_MAX_SIZE /
996                                                   sizeof(uint64_t));
997     static int initialized              = 0;
998     ucs_status_t status                 = UCS_OK;
999     static int pagemap_fd;
1000     uint64_t *data;
1001     unsigned page_num;
1002 
1003     if (!initialized) {
1004         pagemap_fd = open(ucs_pagemap_file, O_RDONLY);
1005         if (pagemap_fd < 0) {
1006             ucs_warn("failed to open %s: %m", ucs_pagemap_file);
1007         }
1008         initialized = 1;
1009     }
1010 
1011     if (pagemap_fd < 0) {
1012         return UCS_ERR_IO_ERROR; /* could not open file */
1013     }
1014 
1015     data = ucs_alloca(ucs_min(UCS_SYS_ENUM_PFN_ELEM_CNT, page_count) *
1016                       sizeof(*data));
1017 
1018     for (page_num = 0; (page_num < page_count) && (status == UCS_OK);
1019          page_num += UCS_SYS_ENUM_PFN_ELEM_CNT) {
1020          status = ucs_sys_enum_pfn_internal(pagemap_fd, page_num, data, address,
1021                                             ucs_min(UCS_SYS_ENUM_PFN_ELEM_CNT,
1022                                                     page_count - page_num),
1023                                             cb, ctx);
1024     }
1025 
1026     return status;
1027 }
1028 
ucs_sys_get_pfn_cb(unsigned page_number,unsigned long pfn,void * ctx)1029 static void ucs_sys_get_pfn_cb(unsigned page_number, unsigned long pfn,
1030                                void *ctx)
1031 {
1032     ((unsigned long*)ctx)[page_number] = pfn;
1033 }
1034 
ucs_sys_get_pfn(uintptr_t address,unsigned page_count,unsigned long * data)1035 ucs_status_t ucs_sys_get_pfn(uintptr_t address, unsigned page_count,
1036                              unsigned long *data)
1037 {
1038     return ucs_sys_enum_pfn(address, page_count, ucs_sys_get_pfn_cb, data);
1039 }
1040 
ucs_sys_fcntl_modfl(int fd,int add,int rem)1041 ucs_status_t ucs_sys_fcntl_modfl(int fd, int add, int rem)
1042 {
1043     int oldfl, ret;
1044 
1045     oldfl = fcntl(fd, F_GETFL);
1046     if (oldfl < 0) {
1047         ucs_error("fcntl(fd=%d, F_GETFL) returned %d: %m", fd, oldfl);
1048         return UCS_ERR_IO_ERROR;
1049     }
1050 
1051     ret = fcntl(fd, F_SETFL, (oldfl | add) & ~rem);
1052     if (ret < 0) {
1053         ucs_error("fcntl(fd=%d, F_SETFL) returned %d: %m", fd, ret);
1054         return UCS_ERR_IO_ERROR;
1055     }
1056 
1057     return UCS_OK;
1058 }
1059 
ucs_get_tid(void)1060 pid_t ucs_get_tid(void)
1061 {
1062 #ifdef SYS_gettid
1063     return syscall(SYS_gettid);
1064 #elif defined(HAVE_SYS_THR_H)
1065     long id;
1066 
1067     thr_self(&id);
1068     return (id);
1069 #else
1070 #error "Port me"
1071 #endif
1072 }
1073 
ucs_tgkill(int tgid,int tid,int sig)1074 int ucs_tgkill(int tgid, int tid, int sig)
1075 {
1076 #ifdef SYS_tgkill
1077     return syscall(SYS_tgkill, tgid, tid, sig);
1078 #elif defined(HAVE_SYS_THR_H)
1079     return (thr_kill2(tgid, tid, sig));
1080 #else
1081 #error "Port me"
1082 #endif
1083 }
1084 
ucs_get_cpuinfo_clock_freq(const char * header,double scale)1085 double ucs_get_cpuinfo_clock_freq(const char *header, double scale)
1086 {
1087     double value = 0.0;
1088     double m;
1089     int rc;
1090     FILE* f;
1091     char buf[256];
1092     char fmt[256];
1093     int warn;
1094 
1095     f = fopen("/proc/cpuinfo","r");
1096     if (!f) {
1097         return 0.0;
1098     }
1099 
1100     snprintf(fmt, sizeof(fmt), "%s : %%lf ", header);
1101 
1102     warn = 0;
1103     while (fgets(buf, sizeof(buf), f)) {
1104 
1105         rc = sscanf(buf, fmt, &m);
1106         if (rc != 1) {
1107             continue;
1108         }
1109 
1110         if (value == 0.0) {
1111             value = m;
1112             continue;
1113         }
1114 
1115         if (value != m) {
1116             value = ucs_max(value,m);
1117             warn = 1;
1118         }
1119     }
1120     fclose(f);
1121 
1122     if (warn) {
1123         ucs_debug("Conflicting CPU frequencies detected, using: %.2f", value);
1124     }
1125 
1126     return value * scale;
1127 }
1128 
ucs_sys_realloc(void * old_ptr,size_t old_length,size_t new_length)1129 void *ucs_sys_realloc(void *old_ptr, size_t old_length, size_t new_length)
1130 {
1131     void *ptr;
1132 
1133     new_length = ucs_align_up_pow2(new_length, ucs_get_page_size());
1134     if (old_ptr == NULL) {
1135         /* Note: Must pass the 0 offset as "long", otherwise it will be
1136          * partially undefined when converted to syscall arguments */
1137         ptr = (void*)syscall(__NR_mmap, NULL, new_length, PROT_READ|PROT_WRITE,
1138                              MAP_PRIVATE|MAP_ANONYMOUS, -1, 0ul);
1139         if (ptr == MAP_FAILED) {
1140             ucs_log_fatal_error("mmap(NULL, %zu, READ|WRITE, PRIVATE|ANON) failed: %m",
1141                                 new_length);
1142             return NULL;
1143         }
1144     } else {
1145         old_length = ucs_align_up_pow2(old_length, ucs_get_page_size());
1146         ptr = (void*)syscall(__NR_mremap, old_ptr, old_length, new_length,
1147                              MREMAP_MAYMOVE);
1148         if (ptr == MAP_FAILED) {
1149             ucs_log_fatal_error("mremap(%p, %zu, %zu, MAYMOVE) failed: %m",
1150                                 old_ptr, old_length, new_length);
1151             return NULL;
1152         }
1153     }
1154 
1155     return ptr;
1156 }
1157 
ucs_sys_free(void * ptr,size_t length)1158 void ucs_sys_free(void *ptr, size_t length)
1159 {
1160     int ret;
1161 
1162     if (ptr != NULL) {
1163         length = ucs_align_up_pow2(length, ucs_get_page_size());
1164         ret = syscall(__NR_munmap, ptr, length);
1165         if (ret) {
1166             ucs_log_fatal_error("munmap(%p, %zu) failed: %m", ptr, length);
1167         }
1168     }
1169 }
1170 
ucs_make_affinity_str(const ucs_sys_cpuset_t * cpuset,char * str,size_t len)1171 char* ucs_make_affinity_str(const ucs_sys_cpuset_t *cpuset, char *str, size_t len)
1172 {
1173     int i = 0, prev = -1;
1174     char *p = str;
1175 
1176     for (i = 0; i < CPU_SETSIZE; i++) {
1177         if (CPU_ISSET(i, cpuset)) {
1178             if (prev < 0) {
1179                 prev = i;
1180             }
1181         } else {
1182             if (prev >= 0) {
1183                 if (prev == i - 1) {
1184                     p += snprintf(p, str + len - p, "%d,", prev);
1185                 } else {
1186                     p += snprintf(p, str + len - p, "%d-%d,", prev, i - 1);
1187                 }
1188             }
1189             if (p > str + len) {
1190                 p = str + len - 4;
1191                 while (*p != ',') {
1192                     p--;
1193                 }
1194                 sprintf(p, "...");
1195                 return str;
1196             }
1197             prev = -1;
1198         }
1199     }
1200 
1201     *(--p) = 0;
1202     return str;
1203 }
1204 
ucs_sys_setaffinity(ucs_sys_cpuset_t * cpuset)1205 int ucs_sys_setaffinity(ucs_sys_cpuset_t *cpuset)
1206 {
1207     int ret;
1208 
1209 #if defined(HAVE_SCHED_SETAFFINITY)
1210     ret = sched_setaffinity(0, sizeof(*cpuset), cpuset);
1211 #elif defined(HAVE_CPUSET_SETAFFINITY)
1212     ret = cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, getpid(),
1213                              sizeof(*cpuset), cpuset);
1214 #else
1215 #error "Port me"
1216 #endif
1217     return ret;
1218 }
1219 
ucs_sys_getaffinity(ucs_sys_cpuset_t * cpuset)1220 int ucs_sys_getaffinity(ucs_sys_cpuset_t *cpuset)
1221 {
1222     int ret;
1223 
1224 #if defined(HAVE_SCHED_GETAFFINITY)
1225     ret = sched_getaffinity(0, sizeof(*cpuset), cpuset);
1226 #elif defined(HAVE_CPUSET_GETAFFINITY)
1227     ret = cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, getpid(),
1228                              sizeof(*cpuset), cpuset);
1229 #else
1230 #error "Port me"
1231 #endif
1232     return ret;
1233 }
1234 
ucs_sys_cpuset_copy(ucs_cpu_set_t * dst,const ucs_sys_cpuset_t * src)1235 void ucs_sys_cpuset_copy(ucs_cpu_set_t *dst, const ucs_sys_cpuset_t *src)
1236 {
1237     int c;
1238 
1239     UCS_CPU_ZERO(dst);
1240     for (c = 0; c < UCS_CPU_SETSIZE; ++c) {
1241         if (CPU_ISSET(c, src)) {
1242             UCS_CPU_SET(c, dst);
1243         }
1244     }
1245 }
1246 
ucs_sys_get_ns(ucs_sys_namespace_type_t ns)1247 ucs_sys_ns_t ucs_sys_get_ns(ucs_sys_namespace_type_t ns)
1248 {
1249     char filename[MAXPATHLEN];
1250     int res;
1251     struct stat st;
1252 
1253     if (ns >= UCS_SYS_NS_TYPE_LAST) {
1254         return 0;
1255     }
1256 
1257     snprintf(filename, sizeof(filename), "%s/%s", UCS_PROCESS_NS_DIR,
1258              ucs_sys_namespace_info[ns].name);
1259 
1260     res = stat(filename, &st);
1261     if (res == 0) {
1262         return (ucs_sys_ns_t)st.st_ino;
1263     }
1264 
1265     return ucs_sys_namespace_info[ns].dflt;
1266 }
1267 
ucs_sys_ns_is_default(ucs_sys_namespace_type_t ns)1268 int ucs_sys_ns_is_default(ucs_sys_namespace_type_t ns)
1269 {
1270     return ucs_sys_get_ns(ns) == ucs_sys_namespace_info[ns].dflt;
1271 }
1272 
ucs_sys_get_boot_id(uint64_t * high,uint64_t * low)1273 ucs_status_t ucs_sys_get_boot_id(uint64_t *high, uint64_t *low)
1274 {
1275     static struct {
1276         uint64_t     high;
1277         uint64_t     low;
1278     } boot_id                        = {0, 0};
1279 
1280     static ucs_init_once_t init_once = UCS_INIT_ONCE_INITIALIZER;
1281     static ucs_status_t status       = UCS_ERR_IO_ERROR;
1282     char bootid_str[256];
1283     ssize_t size;
1284     uint32_t v1;
1285     uint16_t v2;
1286     uint16_t v3;
1287     uint16_t v4;
1288     uint8_t v5[6];
1289     int res;
1290     int i;
1291 
1292     UCS_INIT_ONCE(&init_once) {
1293         size = ucs_read_file_str(bootid_str, sizeof(bootid_str), 1,
1294                                  "%s", UCS_PROCESS_BOOTID_FILE);
1295         if (size <= 0) {
1296             continue; /* jump out of INIT_ONCE section */
1297         }
1298 
1299         res = sscanf(bootid_str, UCS_PROCESS_BOOTID_FMT,
1300                      &v1, &v2, &v3, &v4,
1301                      &v5[0], &v5[1], &v5[2],
1302                      &v5[3], &v5[4], &v5[5]);
1303         if (res == 10) { /* 10 values should be scanned */
1304             status       = UCS_OK;
1305             boot_id.low  = ((uint64_t)v1) | ((uint64_t)v2 << 32) |
1306                            ((uint64_t)v3 << 48);
1307             boot_id.high = v4;
1308             for (i = 0; i < ucs_array_size(v5); i++) {
1309                 boot_id.high |= (uint64_t)v5[i] << (16 + (i * 8));
1310             }
1311         }
1312     }
1313 
1314     if (status == UCS_OK) {
1315         *high = boot_id.high;
1316         *low  = boot_id.low;
1317     }
1318 
1319     return status;
1320 }
1321 
ucs_sys_readdir(const char * path,ucs_sys_readdir_cb_t cb,void * ctx)1322 ucs_status_t ucs_sys_readdir(const char *path, ucs_sys_readdir_cb_t cb, void *ctx)
1323 {
1324     ucs_status_t res = 0;
1325     DIR *dir;
1326     struct dirent *entry;
1327     struct dirent *entry_out;
1328     size_t entry_len;
1329 
1330     dir = opendir(path);
1331     if (dir == NULL) {
1332         return UCS_ERR_NO_ELEM; /* failed to open directory */
1333     }
1334 
1335     entry_len = ucs_offsetof(struct dirent, d_name) +
1336                 fpathconf(dirfd(dir), _PC_NAME_MAX) + 1;
1337     entry     = (struct dirent*)malloc(entry_len);
1338     if (entry == NULL) {
1339         res = UCS_ERR_NO_MEMORY;
1340         goto failed_no_mem;
1341     }
1342 
1343     while (!readdir_r(dir, entry, &entry_out) && (entry_out != NULL)) {
1344         res = cb(entry, ctx);
1345         if (res != UCS_OK) {
1346             break;
1347         }
1348     }
1349 
1350     free(entry);
1351 failed_no_mem:
1352     closedir(dir);
1353     return res;
1354 }
1355 
ucs_sys_enum_threads_cb(struct dirent * entry,void * _ctx)1356 static ucs_status_t ucs_sys_enum_threads_cb(struct dirent *entry, void *_ctx)
1357 {
1358     ucs_sys_enum_threads_t *ctx = (ucs_sys_enum_threads_t*)_ctx;
1359 
1360     return strncmp(entry->d_name, ".", 1) ?
1361            ctx->cb((pid_t)atoi(entry->d_name), ctx->ctx) : 0;
1362 }
1363 
ucs_sys_enum_threads(ucs_sys_enum_threads_cb_t cb,void * ctx)1364 ucs_status_t ucs_sys_enum_threads(ucs_sys_enum_threads_cb_t cb, void *ctx)
1365 {
1366     static const char *task_dir  = "/proc/self/task";
1367     ucs_sys_enum_threads_t param = {.ctx = ctx, .cb = cb};
1368 
1369     return ucs_sys_readdir(task_dir, &ucs_sys_enum_threads_cb, &param);
1370 }
1371