Mercurial
comparison third_party/libuv/src/unix/os390.c @ 160:948de3f54cea
[ThirdParty] Added libuv
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Wed, 14 Jan 2026 19:39:52 -0800 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 159:05cf9467a1c3 | 160:948de3f54cea |
|---|---|
| 1 /* Copyright libuv project contributors. All rights reserved. | |
| 2 * | |
| 3 * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 4 * of this software and associated documentation files (the "Software"), to | |
| 5 * deal in the Software without restriction, including without limitation the | |
| 6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | |
| 7 * sell copies of the Software, and to permit persons to whom the Software is | |
| 8 * furnished to do so, subject to the following conditions: | |
| 9 * | |
| 10 * The above copyright notice and this permission notice shall be included in | |
| 11 * all copies or substantial portions of the Software. | |
| 12 * | |
| 13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
| 18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | |
| 19 * IN THE SOFTWARE. | |
| 20 */ | |
| 21 | |
| 22 #include "uv.h" | |
| 23 #include "internal.h" | |
| 24 #include <sys/ioctl.h> | |
| 25 #include <net/if.h> | |
| 26 #include <utmpx.h> | |
| 27 #include <unistd.h> | |
| 28 #include <sys/ps.h> | |
| 29 #include <builtins.h> | |
| 30 #include <termios.h> | |
| 31 #include <sys/msg.h> | |
| 32 #include <sys/resource.h> | |
| 33 #include "zos-base.h" | |
| 34 #include "zos-sys-info.h" | |
| 35 #if defined(__clang__) | |
| 36 #include "csrsic.h" | |
| 37 #else | |
| 38 #include "//'SYS1.SAMPLIB(CSRSIC)'" | |
| 39 #endif | |
| 40 | |
| 41 #define CVT_PTR 0x10 | |
| 42 #define PSA_PTR 0x00 | |
| 43 #define CSD_OFFSET 0x294 | |
| 44 | |
| 45 /* | |
| 46 Long-term average CPU service used by this logical partition, | |
| 47 in millions of service units per hour. If this value is above | |
| 48 the partition's defined capacity, the partition will be capped. | |
| 49 It is calculated using the physical CPU adjustment factor | |
| 50 (RCTPCPUA) so it may not match other measures of service which | |
| 51 are based on the logical CPU adjustment factor. It is available | |
| 52 if the hardware supports LPAR cluster. | |
| 53 */ | |
| 54 #define RCTLACS_OFFSET 0xC4 | |
| 55 | |
| 56 /* 32-bit count of alive CPUs. This includes both CPs and IFAs */ | |
| 57 #define CSD_NUMBER_ONLINE_CPUS 0xD4 | |
| 58 | |
| 59 /* Address of system resources manager (SRM) control table */ | |
| 60 #define CVTOPCTP_OFFSET 0x25C | |
| 61 | |
| 62 /* Address of the RCT table */ | |
| 63 #define RMCTRCT_OFFSET 0xE4 | |
| 64 | |
| 65 /* Address of the rsm control and enumeration area. */ | |
| 66 #define CVTRCEP_OFFSET 0x490 | |
| 67 | |
| 68 /* Total number of frames currently on all available frame queues. */ | |
| 69 #define RCEAFC_OFFSET 0x088 | |
| 70 | |
| 71 /* Pointer to the home (current) ASCB. */ | |
| 72 #define PSAAOLD 0x224 | |
| 73 | |
| 74 /* Pointer to rsm address space block extension. */ | |
| 75 #define ASCBRSME 0x16C | |
| 76 | |
| 77 /* | |
| 78 NUMBER OF FRAMES CURRENTLY IN USE BY THIS ADDRESS SPACE. | |
| 79 It does not include 2G frames. | |
| 80 */ | |
| 81 #define RAXFMCT 0x2C | |
| 82 | |
| 83 /* Thread Entry constants */ | |
| 84 #define PGTH_CURRENT 1 | |
| 85 #define PGTH_LEN 26 | |
| 86 #define PGTHAPATH 0x20 | |
| 87 #pragma linkage(BPX4GTH, OS) | |
| 88 #pragma linkage(BPX1GTH, OS) | |
| 89 | |
| 90 /* TOD Clock resolution in nanoseconds */ | |
| 91 #define TOD_RES 4.096 | |
| 92 | |
| 93 typedef unsigned data_area_ptr_assign_type; | |
| 94 | |
| 95 typedef union { | |
| 96 struct { | |
| 97 #if defined(_LP64) | |
| 98 data_area_ptr_assign_type lower; | |
| 99 #endif | |
| 100 data_area_ptr_assign_type assign; | |
| 101 }; | |
| 102 char* deref; | |
| 103 } data_area_ptr; | |
| 104 | |
| 105 | |
| 106 void uv_loadavg(double avg[3]) { | |
| 107 /* TODO: implement the following */ | |
| 108 avg[0] = 0; | |
| 109 avg[1] = 0; | |
| 110 avg[2] = 0; | |
| 111 } | |
| 112 | |
| 113 | |
| 114 int uv__platform_loop_init(uv_loop_t* loop) { | |
| 115 uv__os390_epoll* ep; | |
| 116 | |
| 117 ep = epoll_create1(0); | |
| 118 loop->ep = ep; | |
| 119 if (ep == NULL) | |
| 120 return UV__ERR(errno); | |
| 121 | |
| 122 return 0; | |
| 123 } | |
| 124 | |
| 125 | |
| 126 void uv__platform_loop_delete(uv_loop_t* loop) { | |
| 127 if (loop->ep != NULL) { | |
| 128 epoll_queue_close(loop->ep); | |
| 129 loop->ep = NULL; | |
| 130 } | |
| 131 } | |
| 132 | |
| 133 | |
| 134 uint64_t uv__hrtime(uv_clocktype_t type) { | |
| 135 unsigned long long timestamp; | |
| 136 __stckf(×tamp); | |
| 137 /* Convert to nanoseconds */ | |
| 138 return timestamp / TOD_RES; | |
| 139 } | |
| 140 | |
| 141 | |
| 142 static int getexe(char* buf, size_t len) { | |
| 143 return uv__strscpy(buf, __getargv()[0], len); | |
| 144 } | |
| 145 | |
| 146 | |
| 147 /* | |
| 148 * We could use a static buffer for the path manipulations that we need outside | |
| 149 * of the function, but this function could be called by multiple consumers and | |
| 150 * we don't want to potentially create a race condition in the use of snprintf. | |
| 151 * There is no direct way of getting the exe path in zOS - either through /procfs | |
| 152 * or through some libc APIs. The below approach is to parse the argv[0]'s pattern | |
| 153 * and use it in conjunction with PATH environment variable to craft one. | |
| 154 */ | |
| 155 int uv_exepath(char* buffer, size_t* size) { | |
| 156 int res; | |
| 157 char args[PATH_MAX]; | |
| 158 int pid; | |
| 159 | |
| 160 if (buffer == NULL || size == NULL || *size == 0) | |
| 161 return UV_EINVAL; | |
| 162 | |
| 163 res = getexe(args, sizeof(args)); | |
| 164 if (res < 0) | |
| 165 return UV_EINVAL; | |
| 166 | |
| 167 return uv__search_path(args, buffer, size); | |
| 168 } | |
| 169 | |
| 170 | |
| 171 uint64_t uv_get_free_memory(void) { | |
| 172 uint64_t freeram; | |
| 173 | |
| 174 data_area_ptr cvt = {0}; | |
| 175 data_area_ptr rcep = {0}; | |
| 176 cvt.assign = *(data_area_ptr_assign_type*)(CVT_PTR); | |
| 177 rcep.assign = *(data_area_ptr_assign_type*)(cvt.deref + CVTRCEP_OFFSET); | |
| 178 freeram = (uint64_t)*((uint32_t*)(rcep.deref + RCEAFC_OFFSET)) * 4096; | |
| 179 return freeram; | |
| 180 } | |
| 181 | |
| 182 | |
| 183 uint64_t uv_get_total_memory(void) { | |
| 184 /* Use CVTRLSTG to get the size of actual real storage online at IPL in K. */ | |
| 185 return (uint64_t)((int)((char *__ptr32 *__ptr32 *)0)[4][214]) * 1024; | |
| 186 } | |
| 187 | |
| 188 | |
| 189 uint64_t uv_get_constrained_memory(void) { | |
| 190 struct rlimit rl; | |
| 191 | |
| 192 /* RLIMIT_MEMLIMIT return value is in megabytes rather than bytes. */ | |
| 193 if (getrlimit(RLIMIT_MEMLIMIT, &rl) == 0) | |
| 194 return rl.rlim_cur * 1024 * 1024; | |
| 195 | |
| 196 return 0; /* There is no memory limit set. */ | |
| 197 } | |
| 198 | |
| 199 | |
| 200 uint64_t uv_get_available_memory(void) { | |
| 201 return uv_get_free_memory(); | |
| 202 } | |
| 203 | |
| 204 | |
| 205 int uv_resident_set_memory(size_t* rss) { | |
| 206 char* ascb; | |
| 207 char* rax; | |
| 208 size_t nframes; | |
| 209 | |
| 210 ascb = *(char* __ptr32 *)(PSA_PTR + PSAAOLD); | |
| 211 rax = *(char* __ptr32 *)(ascb + ASCBRSME); | |
| 212 nframes = *(unsigned int*)(rax + RAXFMCT); | |
| 213 | |
| 214 *rss = nframes * sysconf(_SC_PAGESIZE); | |
| 215 return 0; | |
| 216 } | |
| 217 | |
| 218 | |
| 219 int uv_uptime(double* uptime) { | |
| 220 struct utmpx u ; | |
| 221 struct utmpx *v; | |
| 222 time64_t t; | |
| 223 | |
| 224 u.ut_type = BOOT_TIME; | |
| 225 v = getutxid(&u); | |
| 226 if (v == NULL) | |
| 227 return -1; | |
| 228 *uptime = difftime64(time64(&t), v->ut_tv.tv_sec); | |
| 229 return 0; | |
| 230 } | |
| 231 | |
| 232 | |
| 233 int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { | |
| 234 uv_cpu_info_t* cpu_info; | |
| 235 int idx; | |
| 236 siv1v2 info; | |
| 237 data_area_ptr cvt = {0}; | |
| 238 data_area_ptr csd = {0}; | |
| 239 data_area_ptr rmctrct = {0}; | |
| 240 data_area_ptr cvtopctp = {0}; | |
| 241 int cpu_usage_avg; | |
| 242 | |
| 243 cvt.assign = *(data_area_ptr_assign_type*)(CVT_PTR); | |
| 244 | |
| 245 csd.assign = *((data_area_ptr_assign_type *) (cvt.deref + CSD_OFFSET)); | |
| 246 cvtopctp.assign = *((data_area_ptr_assign_type *) (cvt.deref + CVTOPCTP_OFFSET)); | |
| 247 rmctrct.assign = *((data_area_ptr_assign_type *) (cvtopctp.deref + RMCTRCT_OFFSET)); | |
| 248 | |
| 249 *count = *((int*) (csd.deref + CSD_NUMBER_ONLINE_CPUS)); | |
| 250 cpu_usage_avg = *((unsigned short int*) (rmctrct.deref + RCTLACS_OFFSET)); | |
| 251 | |
| 252 *cpu_infos = uv__malloc(*count * sizeof(uv_cpu_info_t)); | |
| 253 if (!*cpu_infos) | |
| 254 return UV_ENOMEM; | |
| 255 | |
| 256 cpu_info = *cpu_infos; | |
| 257 idx = 0; | |
| 258 while (idx < *count) { | |
| 259 cpu_info->speed = *(int*)(info.siv1v2si22v1.si22v1cpucapability); | |
| 260 cpu_info->model = uv__malloc(ZOSCPU_MODEL_LENGTH + 1); | |
| 261 if (cpu_info->model == NULL) { | |
| 262 uv_free_cpu_info(*cpu_infos, idx); | |
| 263 return UV_ENOMEM; | |
| 264 } | |
| 265 __get_cpu_model(cpu_info->model, ZOSCPU_MODEL_LENGTH + 1); | |
| 266 cpu_info->cpu_times.user = cpu_usage_avg; | |
| 267 /* TODO: implement the following */ | |
| 268 cpu_info->cpu_times.sys = 0; | |
| 269 cpu_info->cpu_times.idle = 0; | |
| 270 cpu_info->cpu_times.irq = 0; | |
| 271 cpu_info->cpu_times.nice = 0; | |
| 272 ++cpu_info; | |
| 273 ++idx; | |
| 274 } | |
| 275 | |
| 276 return 0; | |
| 277 } | |
| 278 | |
| 279 | |
| 280 static int uv__interface_addresses_v6(uv_interface_address_t** addresses, | |
| 281 int* count) { | |
| 282 uv_interface_address_t* address; | |
| 283 int sockfd; | |
| 284 int maxsize; | |
| 285 __net_ifconf6header_t ifc; | |
| 286 __net_ifconf6entry_t* ifr; | |
| 287 __net_ifconf6entry_t* p; | |
| 288 unsigned int i; | |
| 289 int count_names; | |
| 290 unsigned char netmask[16] = {0}; | |
| 291 | |
| 292 *count = 0; | |
| 293 /* Assume maximum buffer size allowable */ | |
| 294 maxsize = 16384; | |
| 295 | |
| 296 if (0 > (sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP))) | |
| 297 return UV__ERR(errno); | |
| 298 | |
| 299 ifc.__nif6h_buffer = uv__calloc(1, maxsize); | |
| 300 | |
| 301 if (ifc.__nif6h_buffer == NULL) { | |
| 302 uv__close(sockfd); | |
| 303 return UV_ENOMEM; | |
| 304 } | |
| 305 | |
| 306 ifc.__nif6h_version = 1; | |
| 307 ifc.__nif6h_buflen = maxsize; | |
| 308 | |
| 309 if (ioctl(sockfd, SIOCGIFCONF6, &ifc) == -1) { | |
| 310 /* This will error on a system that does not support IPv6. However, we want | |
| 311 * to treat this as there being 0 interfaces so we can continue to get IPv4 | |
| 312 * interfaces in uv_interface_addresses(). So return 0 instead of the error. | |
| 313 */ | |
| 314 uv__free(ifc.__nif6h_buffer); | |
| 315 uv__close(sockfd); | |
| 316 errno = 0; | |
| 317 return 0; | |
| 318 } | |
| 319 | |
| 320 ifr = (__net_ifconf6entry_t*)(ifc.__nif6h_buffer); | |
| 321 while ((char*)ifr < (char*)ifc.__nif6h_buffer + ifc.__nif6h_buflen) { | |
| 322 p = ifr; | |
| 323 ifr = (__net_ifconf6entry_t*)((char*)ifr + ifc.__nif6h_entrylen); | |
| 324 | |
| 325 if (!(p->__nif6e_addr.sin6_family == AF_INET6)) | |
| 326 continue; | |
| 327 | |
| 328 if (!(p->__nif6e_flags & _NIF6E_FLAGS_ON_LINK_ACTIVE)) | |
| 329 continue; | |
| 330 | |
| 331 ++(*count); | |
| 332 } | |
| 333 | |
| 334 if ((*count) == 0) { | |
| 335 uv__free(ifc.__nif6h_buffer); | |
| 336 uv__close(sockfd); | |
| 337 return 0; | |
| 338 } | |
| 339 | |
| 340 /* Alloc the return interface structs */ | |
| 341 *addresses = uv__calloc(1, *count * sizeof(uv_interface_address_t)); | |
| 342 if (!(*addresses)) { | |
| 343 uv__free(ifc.__nif6h_buffer); | |
| 344 uv__close(sockfd); | |
| 345 return UV_ENOMEM; | |
| 346 } | |
| 347 address = *addresses; | |
| 348 | |
| 349 count_names = 0; | |
| 350 ifr = (__net_ifconf6entry_t*)(ifc.__nif6h_buffer); | |
| 351 while ((char*)ifr < (char*)ifc.__nif6h_buffer + ifc.__nif6h_buflen) { | |
| 352 p = ifr; | |
| 353 ifr = (__net_ifconf6entry_t*)((char*)ifr + ifc.__nif6h_entrylen); | |
| 354 | |
| 355 if (!(p->__nif6e_addr.sin6_family == AF_INET6)) | |
| 356 continue; | |
| 357 | |
| 358 if (!(p->__nif6e_flags & _NIF6E_FLAGS_ON_LINK_ACTIVE)) | |
| 359 continue; | |
| 360 | |
| 361 /* All conditions above must match count loop */ | |
| 362 | |
| 363 i = 0; | |
| 364 /* Ignore EBCDIC space (0x40) padding in name */ | |
| 365 while (i < ARRAY_SIZE(p->__nif6e_name) && | |
| 366 p->__nif6e_name[i] != 0x40 && | |
| 367 p->__nif6e_name[i] != 0) | |
| 368 ++i; | |
| 369 address->name = uv__malloc(i + 1); | |
| 370 if (address->name == NULL) { | |
| 371 uv_free_interface_addresses(*addresses, count_names); | |
| 372 uv__free(ifc.__nif6h_buffer); | |
| 373 uv__close(sockfd); | |
| 374 return UV_ENOMEM; | |
| 375 } | |
| 376 memcpy(address->name, p->__nif6e_name, i); | |
| 377 address->name[i] = '\0'; | |
| 378 __e2a_s(address->name); | |
| 379 count_names++; | |
| 380 | |
| 381 address->address.address6 = *((struct sockaddr_in6*) &p->__nif6e_addr); | |
| 382 | |
| 383 for (i = 0; i < (p->__nif6e_prefixlen / 8); i++) | |
| 384 netmask[i] = 0xFF; | |
| 385 | |
| 386 if (p->__nif6e_prefixlen % 8) | |
| 387 netmask[i] = 0xFF << (8 - (p->__nif6e_prefixlen % 8)); | |
| 388 | |
| 389 address->netmask.netmask6.sin6_len = p->__nif6e_prefixlen; | |
| 390 memcpy(&(address->netmask.netmask6.sin6_addr), netmask, 16); | |
| 391 address->netmask.netmask6.sin6_family = AF_INET6; | |
| 392 | |
| 393 address->is_internal = p->__nif6e_flags & _NIF6E_FLAGS_LOOPBACK ? 1 : 0; | |
| 394 address++; | |
| 395 } | |
| 396 | |
| 397 uv__free(ifc.__nif6h_buffer); | |
| 398 uv__close(sockfd); | |
| 399 return 0; | |
| 400 } | |
| 401 | |
| 402 | |
| 403 int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { | |
| 404 uv_interface_address_t* address; | |
| 405 int sockfd; | |
| 406 int maxsize; | |
| 407 struct ifconf ifc; | |
| 408 struct ifreq flg; | |
| 409 struct ifreq* ifr; | |
| 410 struct ifreq* p; | |
| 411 uv_interface_address_t* addresses_v6; | |
| 412 int count_v6; | |
| 413 unsigned int i; | |
| 414 int rc; | |
| 415 int count_names; | |
| 416 | |
| 417 *count = 0; | |
| 418 *addresses = NULL; | |
| 419 | |
| 420 /* get the ipv6 addresses first */ | |
| 421 if ((rc = uv__interface_addresses_v6(&addresses_v6, &count_v6)) != 0) | |
| 422 return rc; | |
| 423 | |
| 424 /* now get the ipv4 addresses */ | |
| 425 | |
| 426 /* Assume maximum buffer size allowable */ | |
| 427 maxsize = 16384; | |
| 428 | |
| 429 sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); | |
| 430 if (0 > sockfd) { | |
| 431 if (count_v6) | |
| 432 uv_free_interface_addresses(addresses_v6, count_v6); | |
| 433 return UV__ERR(errno); | |
| 434 } | |
| 435 | |
| 436 ifc.ifc_req = uv__calloc(1, maxsize); | |
| 437 | |
| 438 if (ifc.ifc_req == NULL) { | |
| 439 if (count_v6) | |
| 440 uv_free_interface_addresses(addresses_v6, count_v6); | |
| 441 uv__close(sockfd); | |
| 442 return UV_ENOMEM; | |
| 443 } | |
| 444 | |
| 445 ifc.ifc_len = maxsize; | |
| 446 | |
| 447 if (ioctl(sockfd, SIOCGIFCONF, &ifc) == -1) { | |
| 448 if (count_v6) | |
| 449 uv_free_interface_addresses(addresses_v6, count_v6); | |
| 450 uv__free(ifc.ifc_req); | |
| 451 uv__close(sockfd); | |
| 452 return UV__ERR(errno); | |
| 453 } | |
| 454 | |
| 455 #define MAX(a,b) (((a)>(b))?(a):(b)) | |
| 456 #define ADDR_SIZE(p) MAX((p).sa_len, sizeof(p)) | |
| 457 | |
| 458 /* Count all up and running ipv4/ipv6 addresses */ | |
| 459 ifr = ifc.ifc_req; | |
| 460 while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) { | |
| 461 p = ifr; | |
| 462 ifr = (struct ifreq*) | |
| 463 ((char*)ifr + sizeof(ifr->ifr_name) + ADDR_SIZE(ifr->ifr_addr)); | |
| 464 | |
| 465 if (!(p->ifr_addr.sa_family == AF_INET6 || | |
| 466 p->ifr_addr.sa_family == AF_INET)) | |
| 467 continue; | |
| 468 | |
| 469 memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name)); | |
| 470 if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) { | |
| 471 if (count_v6) | |
| 472 uv_free_interface_addresses(addresses_v6, count_v6); | |
| 473 uv__free(ifc.ifc_req); | |
| 474 uv__close(sockfd); | |
| 475 return UV__ERR(errno); | |
| 476 } | |
| 477 | |
| 478 if (!(flg.ifr_flags & IFF_UP && flg.ifr_flags & IFF_RUNNING)) | |
| 479 continue; | |
| 480 | |
| 481 (*count)++; | |
| 482 } | |
| 483 | |
| 484 if (*count == 0 && count_v6 == 0) { | |
| 485 uv__free(ifc.ifc_req); | |
| 486 uv__close(sockfd); | |
| 487 return 0; | |
| 488 } | |
| 489 | |
| 490 /* Alloc the return interface structs */ | |
| 491 *addresses = uv__calloc(1, (*count + count_v6) * | |
| 492 sizeof(uv_interface_address_t)); | |
| 493 | |
| 494 if (!(*addresses)) { | |
| 495 if (count_v6) | |
| 496 uv_free_interface_addresses(addresses_v6, count_v6); | |
| 497 uv__free(ifc.ifc_req); | |
| 498 uv__close(sockfd); | |
| 499 return UV_ENOMEM; | |
| 500 } | |
| 501 address = *addresses; | |
| 502 | |
| 503 /* copy over the ipv6 addresses if any are found */ | |
| 504 if (count_v6) { | |
| 505 memcpy(address, addresses_v6, count_v6 * sizeof(uv_interface_address_t)); | |
| 506 address += count_v6; | |
| 507 *count += count_v6; | |
| 508 /* free ipv6 addresses, but keep address names */ | |
| 509 uv__free(addresses_v6); | |
| 510 } | |
| 511 | |
| 512 count_names = *count; | |
| 513 ifr = ifc.ifc_req; | |
| 514 while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) { | |
| 515 p = ifr; | |
| 516 ifr = (struct ifreq*) | |
| 517 ((char*)ifr + sizeof(ifr->ifr_name) + ADDR_SIZE(ifr->ifr_addr)); | |
| 518 | |
| 519 if (!(p->ifr_addr.sa_family == AF_INET6 || | |
| 520 p->ifr_addr.sa_family == AF_INET)) | |
| 521 continue; | |
| 522 | |
| 523 memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name)); | |
| 524 if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) { | |
| 525 uv_free_interface_addresses(*addresses, count_names); | |
| 526 uv__free(ifc.ifc_req); | |
| 527 uv__close(sockfd); | |
| 528 return UV_ENOSYS; | |
| 529 } | |
| 530 | |
| 531 if (!(flg.ifr_flags & IFF_UP && flg.ifr_flags & IFF_RUNNING)) | |
| 532 continue; | |
| 533 | |
| 534 /* All conditions above must match count loop */ | |
| 535 | |
| 536 i = 0; | |
| 537 /* Ignore EBCDIC space (0x40) padding in name */ | |
| 538 while (i < ARRAY_SIZE(p->ifr_name) && | |
| 539 p->ifr_name[i] != 0x40 && | |
| 540 p->ifr_name[i] != 0) | |
| 541 ++i; | |
| 542 address->name = uv__malloc(i + 1); | |
| 543 if (address->name == NULL) { | |
| 544 uv_free_interface_addresses(*addresses, count_names); | |
| 545 uv__free(ifc.ifc_req); | |
| 546 uv__close(sockfd); | |
| 547 return UV_ENOMEM; | |
| 548 } | |
| 549 memcpy(address->name, p->ifr_name, i); | |
| 550 address->name[i] = '\0'; | |
| 551 __e2a_s(address->name); | |
| 552 count_names++; | |
| 553 | |
| 554 address->address.address4 = *((struct sockaddr_in*) &p->ifr_addr); | |
| 555 | |
| 556 if (ioctl(sockfd, SIOCGIFNETMASK, p) == -1) { | |
| 557 uv_free_interface_addresses(*addresses, count_names); | |
| 558 uv__free(ifc.ifc_req); | |
| 559 uv__close(sockfd); | |
| 560 return UV__ERR(errno); | |
| 561 } | |
| 562 | |
| 563 address->netmask.netmask4 = *((struct sockaddr_in*) &p->ifr_addr); | |
| 564 address->netmask.netmask4.sin_family = AF_INET; | |
| 565 address->is_internal = flg.ifr_flags & IFF_LOOPBACK ? 1 : 0; | |
| 566 address++; | |
| 567 } | |
| 568 | |
| 569 #undef ADDR_SIZE | |
| 570 #undef MAX | |
| 571 | |
| 572 uv__free(ifc.ifc_req); | |
| 573 uv__close(sockfd); | |
| 574 return 0; | |
| 575 } | |
| 576 | |
| 577 | |
| 578 void uv_free_interface_addresses(uv_interface_address_t* addresses, | |
| 579 int count) { | |
| 580 int i; | |
| 581 for (i = 0; i < count; ++i) | |
| 582 uv__free(addresses[i].name); | |
| 583 uv__free(addresses); | |
| 584 } | |
| 585 | |
| 586 | |
| 587 void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { | |
| 588 struct epoll_event* events; | |
| 589 struct epoll_event dummy; | |
| 590 uintptr_t i; | |
| 591 uintptr_t nfds; | |
| 592 | |
| 593 assert(loop->watchers != NULL); | |
| 594 assert(fd >= 0); | |
| 595 | |
| 596 events = (struct epoll_event*) loop->watchers[loop->nwatchers]; | |
| 597 nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; | |
| 598 if (events != NULL) | |
| 599 /* Invalidate events with same file descriptor */ | |
| 600 for (i = 0; i < nfds; i++) | |
| 601 if ((int) events[i].fd == fd) | |
| 602 events[i].fd = -1; | |
| 603 | |
| 604 /* Remove the file descriptor from the epoll. */ | |
| 605 if (loop->ep != NULL) | |
| 606 epoll_ctl(loop->ep, EPOLL_CTL_DEL, fd, &dummy); | |
| 607 } | |
| 608 | |
| 609 | |
| 610 int uv__io_check_fd(uv_loop_t* loop, int fd) { | |
| 611 struct pollfd p[1]; | |
| 612 int rv; | |
| 613 | |
| 614 p[0].fd = fd; | |
| 615 p[0].events = POLLIN; | |
| 616 | |
| 617 do | |
| 618 rv = poll(p, 1, 0); | |
| 619 while (rv == -1 && errno == EINTR); | |
| 620 | |
| 621 if (rv == -1) | |
| 622 abort(); | |
| 623 | |
| 624 if (p[0].revents & POLLNVAL) | |
| 625 return -1; | |
| 626 | |
| 627 return 0; | |
| 628 } | |
| 629 | |
| 630 | |
| 631 int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) { | |
| 632 uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT); | |
| 633 return 0; | |
| 634 } | |
| 635 | |
| 636 | |
| 637 static int os390_regfileint(uv_fs_event_t* handle, char* path) { | |
| 638 uv__os390_epoll* ep; | |
| 639 _RFIS reg_struct; | |
| 640 int rc; | |
| 641 | |
| 642 ep = handle->loop->ep; | |
| 643 assert(ep->msg_queue != -1); | |
| 644 | |
| 645 reg_struct.__rfis_cmd = _RFIS_REG; | |
| 646 reg_struct.__rfis_qid = ep->msg_queue; | |
| 647 reg_struct.__rfis_type = 1; | |
| 648 memcpy(reg_struct.__rfis_utok, &handle, sizeof(handle)); | |
| 649 | |
| 650 rc = __w_pioctl(path, _IOCC_REGFILEINT, sizeof(reg_struct), ®_struct); | |
| 651 if (rc != 0) | |
| 652 return UV__ERR(errno); | |
| 653 | |
| 654 memcpy(handle->rfis_rftok, reg_struct.__rfis_rftok, | |
| 655 sizeof(handle->rfis_rftok)); | |
| 656 | |
| 657 return 0; | |
| 658 } | |
| 659 | |
| 660 | |
| 661 int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb, | |
| 662 const char* filename, unsigned int flags) { | |
| 663 char* path; | |
| 664 int rc; | |
| 665 | |
| 666 if (uv__is_active(handle)) | |
| 667 return UV_EINVAL; | |
| 668 | |
| 669 path = uv__strdup(filename); | |
| 670 if (path == NULL) | |
| 671 return UV_ENOMEM; | |
| 672 | |
| 673 rc = os390_regfileint(handle, path); | |
| 674 if (rc != 0) { | |
| 675 uv__free(path); | |
| 676 return rc; | |
| 677 } | |
| 678 | |
| 679 uv__handle_start(handle); | |
| 680 handle->path = path; | |
| 681 handle->cb = cb; | |
| 682 | |
| 683 return 0; | |
| 684 } | |
| 685 | |
| 686 | |
| 687 int uv__fs_event_stop(uv_fs_event_t* handle) { | |
| 688 uv__os390_epoll* ep; | |
| 689 _RFIS reg_struct; | |
| 690 int rc; | |
| 691 | |
| 692 if (!uv__is_active(handle)) | |
| 693 return 0; | |
| 694 | |
| 695 ep = handle->loop->ep; | |
| 696 assert(ep->msg_queue != -1); | |
| 697 | |
| 698 reg_struct.__rfis_cmd = _RFIS_UNREG; | |
| 699 reg_struct.__rfis_qid = ep->msg_queue; | |
| 700 reg_struct.__rfis_type = 1; | |
| 701 memcpy(reg_struct.__rfis_rftok, handle->rfis_rftok, | |
| 702 sizeof(handle->rfis_rftok)); | |
| 703 | |
| 704 /* | |
| 705 * This call will take "/" as the path argument in case we | |
| 706 * don't care to supply the correct path. The system will simply | |
| 707 * ignore it. | |
| 708 */ | |
| 709 rc = __w_pioctl("/", _IOCC_REGFILEINT, sizeof(reg_struct), ®_struct); | |
| 710 if (rc != 0 && errno != EALREADY && errno != ENOENT) | |
| 711 abort(); | |
| 712 | |
| 713 if (handle->path != NULL) { | |
| 714 uv__free(handle->path); | |
| 715 handle->path = NULL; | |
| 716 } | |
| 717 | |
| 718 if (rc != 0 && errno == EALREADY) | |
| 719 return -1; | |
| 720 | |
| 721 uv__handle_stop(handle); | |
| 722 | |
| 723 return 0; | |
| 724 } | |
| 725 | |
| 726 | |
| 727 int uv_fs_event_stop(uv_fs_event_t* handle) { | |
| 728 uv__fs_event_stop(handle); | |
| 729 return 0; | |
| 730 } | |
| 731 | |
| 732 | |
| 733 void uv__fs_event_close(uv_fs_event_t* handle) { | |
| 734 /* | |
| 735 * If we were unable to unregister file interest here, then it is most likely | |
| 736 * that there is a pending queued change notification. When this happens, we | |
| 737 * don't want to complete the close as it will free the underlying memory for | |
| 738 * the handle, causing a use-after-free problem when the event is processed. | |
| 739 * We defer the final cleanup until after the event is consumed in | |
| 740 * os390_message_queue_handler(). | |
| 741 */ | |
| 742 if (uv__fs_event_stop(handle) == 0) | |
| 743 uv__make_close_pending((uv_handle_t*) handle); | |
| 744 } | |
| 745 | |
| 746 | |
| 747 static int os390_message_queue_handler(uv__os390_epoll* ep) { | |
| 748 uv_fs_event_t* handle; | |
| 749 int msglen; | |
| 750 int events; | |
| 751 _RFIM msg; | |
| 752 | |
| 753 if (ep->msg_queue == -1) | |
| 754 return 0; | |
| 755 | |
| 756 msglen = msgrcv(ep->msg_queue, &msg, sizeof(msg), 0, IPC_NOWAIT); | |
| 757 | |
| 758 if (msglen == -1 && errno == ENOMSG) | |
| 759 return 0; | |
| 760 | |
| 761 if (msglen == -1) | |
| 762 abort(); | |
| 763 | |
| 764 events = 0; | |
| 765 if (msg.__rfim_event == _RFIM_ATTR || msg.__rfim_event == _RFIM_WRITE) | |
| 766 events = UV_CHANGE; | |
| 767 else if (msg.__rfim_event == _RFIM_RENAME || msg.__rfim_event == _RFIM_UNLINK) | |
| 768 events = UV_RENAME; | |
| 769 else if (msg.__rfim_event == 156) | |
| 770 /* TODO(gabylb): zos - this event should not happen, need to investigate. | |
| 771 * | |
| 772 * This event seems to occur when the watched file is [re]moved, or an | |
| 773 * editor (like vim) renames then creates the file on save (for vim, that's | |
| 774 * when backupcopy=no|auto). | |
| 775 */ | |
| 776 events = UV_RENAME; | |
| 777 else | |
| 778 /* Some event that we are not interested in. */ | |
| 779 return 0; | |
| 780 | |
| 781 /* `__rfim_utok` is treated as text when it should be treated as binary while | |
| 782 * running in ASCII mode, resulting in an unwanted autoconversion. | |
| 783 */ | |
| 784 __a2e_l(msg.__rfim_utok, sizeof(msg.__rfim_utok)); | |
| 785 handle = *(uv_fs_event_t**)(msg.__rfim_utok); | |
| 786 assert(handle != NULL); | |
| 787 | |
| 788 assert((handle->flags & UV_HANDLE_CLOSED) == 0); | |
| 789 if (uv__is_closing(handle)) { | |
| 790 uv__handle_stop(handle); | |
| 791 uv__make_close_pending((uv_handle_t*) handle); | |
| 792 return 0; | |
| 793 } else if (handle->path == NULL) { | |
| 794 /* _RFIS_UNREG returned EALREADY. */ | |
| 795 uv__handle_stop(handle); | |
| 796 return 0; | |
| 797 } | |
| 798 | |
| 799 /* The file is implicitly unregistered when the change notification is | |
| 800 * sent, only one notification is sent per registration. So we need to | |
| 801 * re-register interest in a file after each change notification we | |
| 802 * receive. | |
| 803 */ | |
| 804 assert(handle->path != NULL); | |
| 805 os390_regfileint(handle, handle->path); | |
| 806 handle->cb(handle, uv__basename_r(handle->path), events, 0); | |
| 807 return 1; | |
| 808 } | |
| 809 | |
| 810 | |
| 811 void uv__io_poll(uv_loop_t* loop, int timeout) { | |
| 812 static const int max_safe_timeout = 1789569; | |
| 813 uv__loop_internal_fields_t* lfields; | |
| 814 struct epoll_event events[1024]; | |
| 815 struct epoll_event* pe; | |
| 816 struct epoll_event e; | |
| 817 uv__os390_epoll* ep; | |
| 818 int have_signals; | |
| 819 int real_timeout; | |
| 820 struct uv__queue* q; | |
| 821 uv__io_t* w; | |
| 822 uint64_t base; | |
| 823 int count; | |
| 824 int nfds; | |
| 825 int fd; | |
| 826 int op; | |
| 827 int i; | |
| 828 int user_timeout; | |
| 829 int reset_timeout; | |
| 830 | |
| 831 if (loop->nfds == 0) { | |
| 832 assert(uv__queue_empty(&loop->watcher_queue)); | |
| 833 return; | |
| 834 } | |
| 835 | |
| 836 lfields = uv__get_internal_fields(loop); | |
| 837 | |
| 838 while (!uv__queue_empty(&loop->watcher_queue)) { | |
| 839 uv_stream_t* stream; | |
| 840 | |
| 841 q = uv__queue_head(&loop->watcher_queue); | |
| 842 uv__queue_remove(q); | |
| 843 uv__queue_init(q); | |
| 844 w = uv__queue_data(q, uv__io_t, watcher_queue); | |
| 845 | |
| 846 assert(w->pevents != 0); | |
| 847 assert(w->fd >= 0); | |
| 848 | |
| 849 stream= container_of(w, uv_stream_t, io_watcher); | |
| 850 | |
| 851 assert(w->fd < (int) loop->nwatchers); | |
| 852 | |
| 853 e.events = w->pevents; | |
| 854 e.fd = w->fd; | |
| 855 | |
| 856 if (w->events == 0) | |
| 857 op = EPOLL_CTL_ADD; | |
| 858 else | |
| 859 op = EPOLL_CTL_MOD; | |
| 860 | |
| 861 /* XXX Future optimization: do EPOLL_CTL_MOD lazily if we stop watching | |
| 862 * events, skip the syscall and squelch the events after epoll_wait(). | |
| 863 */ | |
| 864 if (epoll_ctl(loop->ep, op, w->fd, &e)) { | |
| 865 if (errno != EEXIST) | |
| 866 abort(); | |
| 867 | |
| 868 assert(op == EPOLL_CTL_ADD); | |
| 869 | |
| 870 /* We've reactivated a file descriptor that's been watched before. */ | |
| 871 if (epoll_ctl(loop->ep, EPOLL_CTL_MOD, w->fd, &e)) | |
| 872 abort(); | |
| 873 } | |
| 874 | |
| 875 w->events = w->pevents; | |
| 876 } | |
| 877 | |
| 878 assert(timeout >= -1); | |
| 879 base = loop->time; | |
| 880 count = 48; /* Benchmarks suggest this gives the best throughput. */ | |
| 881 real_timeout = timeout; | |
| 882 int nevents = 0; | |
| 883 have_signals = 0; | |
| 884 | |
| 885 if (lfields->flags & UV_METRICS_IDLE_TIME) { | |
| 886 reset_timeout = 1; | |
| 887 user_timeout = timeout; | |
| 888 timeout = 0; | |
| 889 } else { | |
| 890 reset_timeout = 0; | |
| 891 } | |
| 892 | |
| 893 nfds = 0; | |
| 894 for (;;) { | |
| 895 /* Only need to set the provider_entry_time if timeout != 0. The function | |
| 896 * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME. | |
| 897 */ | |
| 898 if (timeout != 0) | |
| 899 uv__metrics_set_provider_entry_time(loop); | |
| 900 | |
| 901 if (sizeof(int32_t) == sizeof(long) && timeout >= max_safe_timeout) | |
| 902 timeout = max_safe_timeout; | |
| 903 | |
| 904 /* Store the current timeout in a location that's globally accessible so | |
| 905 * other locations like uv__work_done() can determine whether the queue | |
| 906 * of events in the callback were waiting when poll was called. | |
| 907 */ | |
| 908 lfields->current_timeout = timeout; | |
| 909 | |
| 910 nfds = epoll_wait(loop->ep, events, | |
| 911 ARRAY_SIZE(events), timeout); | |
| 912 | |
| 913 /* Update loop->time unconditionally. It's tempting to skip the update when | |
| 914 * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the | |
| 915 * operating system didn't reschedule our process while in the syscall. | |
| 916 */ | |
| 917 base = loop->time; | |
| 918 SAVE_ERRNO(uv__update_time(loop)); | |
| 919 if (nfds == 0) { | |
| 920 assert(timeout != -1); | |
| 921 | |
| 922 if (reset_timeout != 0) { | |
| 923 timeout = user_timeout; | |
| 924 reset_timeout = 0; | |
| 925 } | |
| 926 | |
| 927 if (timeout == -1) | |
| 928 continue; | |
| 929 | |
| 930 if (timeout == 0) | |
| 931 return; | |
| 932 | |
| 933 /* We may have been inside the system call for longer than |timeout| | |
| 934 * milliseconds so we need to update the timestamp to avoid drift. | |
| 935 */ | |
| 936 goto update_timeout; | |
| 937 } | |
| 938 | |
| 939 if (nfds == -1) { | |
| 940 | |
| 941 if (errno != EINTR) | |
| 942 abort(); | |
| 943 | |
| 944 if (reset_timeout != 0) { | |
| 945 timeout = user_timeout; | |
| 946 reset_timeout = 0; | |
| 947 } | |
| 948 | |
| 949 if (timeout == -1) | |
| 950 continue; | |
| 951 | |
| 952 if (timeout == 0) | |
| 953 return; | |
| 954 | |
| 955 /* Interrupted by a signal. Update timeout and poll again. */ | |
| 956 goto update_timeout; | |
| 957 } | |
| 958 | |
| 959 | |
| 960 assert(loop->watchers != NULL); | |
| 961 loop->watchers[loop->nwatchers] = (void*) events; | |
| 962 loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds; | |
| 963 for (i = 0; i < nfds; i++) { | |
| 964 pe = events + i; | |
| 965 fd = pe->fd; | |
| 966 | |
| 967 /* Skip invalidated events, see uv__platform_invalidate_fd */ | |
| 968 if (fd == -1) | |
| 969 continue; | |
| 970 | |
| 971 ep = loop->ep; | |
| 972 if (pe->is_msg) { | |
| 973 os390_message_queue_handler(ep); | |
| 974 nevents++; | |
| 975 continue; | |
| 976 } | |
| 977 | |
| 978 assert(fd >= 0); | |
| 979 assert((unsigned) fd < loop->nwatchers); | |
| 980 | |
| 981 w = loop->watchers[fd]; | |
| 982 | |
| 983 if (w == NULL) { | |
| 984 /* File descriptor that we've stopped watching, disarm it. | |
| 985 * | |
| 986 * Ignore all errors because we may be racing with another thread | |
| 987 * when the file descriptor is closed. | |
| 988 */ | |
| 989 epoll_ctl(loop->ep, EPOLL_CTL_DEL, fd, pe); | |
| 990 continue; | |
| 991 } | |
| 992 | |
| 993 /* Give users only events they're interested in. Prevents spurious | |
| 994 * callbacks when previous callback invocation in this loop has stopped | |
| 995 * the current watcher. Also, filters out events that users has not | |
| 996 * requested us to watch. | |
| 997 */ | |
| 998 pe->events &= w->pevents | POLLERR | POLLHUP; | |
| 999 | |
| 1000 if (pe->events == POLLERR || pe->events == POLLHUP) | |
| 1001 pe->events |= w->pevents & (POLLIN | POLLOUT); | |
| 1002 | |
| 1003 if (pe->events != 0) { | |
| 1004 /* Run signal watchers last. This also affects child process watchers | |
| 1005 * because those are implemented in terms of signal watchers. | |
| 1006 */ | |
| 1007 if (w == &loop->signal_io_watcher) { | |
| 1008 have_signals = 1; | |
| 1009 } else { | |
| 1010 uv__metrics_update_idle_time(loop); | |
| 1011 w->cb(loop, w, pe->events); | |
| 1012 } | |
| 1013 nevents++; | |
| 1014 } | |
| 1015 } | |
| 1016 | |
| 1017 uv__metrics_inc_events(loop, nevents); | |
| 1018 if (reset_timeout != 0) { | |
| 1019 timeout = user_timeout; | |
| 1020 reset_timeout = 0; | |
| 1021 uv__metrics_inc_events_waiting(loop, nevents); | |
| 1022 } | |
| 1023 | |
| 1024 if (have_signals != 0) { | |
| 1025 uv__metrics_update_idle_time(loop); | |
| 1026 loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN); | |
| 1027 } | |
| 1028 | |
| 1029 loop->watchers[loop->nwatchers] = NULL; | |
| 1030 loop->watchers[loop->nwatchers + 1] = NULL; | |
| 1031 | |
| 1032 if (have_signals != 0) | |
| 1033 return; /* Event loop should cycle now so don't poll again. */ | |
| 1034 | |
| 1035 if (nevents != 0) { | |
| 1036 if (nfds == ARRAY_SIZE(events) && --count != 0) { | |
| 1037 /* Poll for more events but don't block this time. */ | |
| 1038 timeout = 0; | |
| 1039 continue; | |
| 1040 } | |
| 1041 return; | |
| 1042 } | |
| 1043 | |
| 1044 if (timeout == 0) | |
| 1045 return; | |
| 1046 | |
| 1047 if (timeout == -1) | |
| 1048 continue; | |
| 1049 | |
| 1050 update_timeout: | |
| 1051 assert(timeout > 0); | |
| 1052 | |
| 1053 real_timeout -= (loop->time - base); | |
| 1054 if (real_timeout <= 0) | |
| 1055 return; | |
| 1056 | |
| 1057 timeout = real_timeout; | |
| 1058 } | |
| 1059 } | |
| 1060 | |
| 1061 | |
| 1062 int uv__io_fork(uv_loop_t* loop) { | |
| 1063 /* | |
| 1064 Nullify the msg queue but don't close it because | |
| 1065 it is still being used by the parent. | |
| 1066 */ | |
| 1067 loop->ep = NULL; | |
| 1068 | |
| 1069 return uv__platform_loop_init(loop); | |
| 1070 } |