xref: /third_party/libuv/src/unix/ibmi.c (revision e66f31c5)
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
25#include <stdio.h>
26#include <stdint.h>
27#include <stdlib.h>
28#include <string.h>
29#include <errno.h>
30
31#include <sys/types.h>
32#include <sys/socket.h>
33#include <sys/ioctl.h>
34#include <net/if.h>
35#include <netinet/in.h>
36#include <arpa/inet.h>
37
38#include <sys/time.h>
39#include <unistd.h>
40#include <fcntl.h>
41#include <utmp.h>
42#include <libgen.h>
43
44#include <sys/protosw.h>
45#include <procinfo.h>
46#include <sys/proc.h>
47#include <sys/procfs.h>
48
49#include <ctype.h>
50
51#include <sys/mntctl.h>
52#include <sys/vmount.h>
53#include <limits.h>
54#include <strings.h>
55#include <sys/vnode.h>
56
57#include <as400_protos.h>
58#include <as400_types.h>
59
60char* original_exepath = NULL;
61uv_mutex_t process_title_mutex;
62uv_once_t process_title_mutex_once = UV_ONCE_INIT;
63
64typedef struct {
65  int bytes_available;
66  int bytes_returned;
67  char current_date_and_time[8];
68  char system_name[8];
69  char elapsed_time[6];
70  char restricted_state_flag;
71  char reserved;
72  int percent_processing_unit_used;
73  int jobs_in_system;
74  int percent_permanent_addresses;
75  int percent_temporary_addresses;
76  int system_asp;
77  int percent_system_asp_used;
78  int total_auxiliary_storage;
79  int current_unprotected_storage_used;
80  int maximum_unprotected_storage_used;
81  int percent_db_capability;
82  int main_storage_size;
83  int number_of_partitions;
84  int partition_identifier;
85  int reserved1;
86  int current_processing_capacity;
87  char processor_sharing_attribute;
88  char reserved2[3];
89  int number_of_processors;
90  int active_jobs_in_system;
91  int active_threads_in_system;
92  int maximum_jobs_in_system;
93  int percent_temporary_256mb_segments_used;
94  int percent_temporary_4gb_segments_used;
95  int percent_permanent_256mb_segments_used;
96  int percent_permanent_4gb_segments_used;
97  int percent_current_interactive_performance;
98  int percent_uncapped_cpu_capacity_used;
99  int percent_shared_processor_pool_used;
100  long main_storage_size_long;
101} SSTS0200;
102
103
104typedef struct {
105  char header[208];
106  unsigned char loca_adapter_address[12];
107} LIND0500;
108
109
110typedef struct {
111  int bytes_provided;
112  int bytes_available;
113  char msgid[7];
114} errcode_s;
115
116
117static const unsigned char e2a[256] = {
118    0, 1, 2, 3, 156, 9, 134, 127, 151, 141, 142, 11, 12, 13, 14, 15,
119    16, 17, 18, 19, 157, 133, 8, 135, 24, 25, 146, 143, 28, 29, 30, 31,
120    128, 129, 130, 131, 132, 10, 23, 27, 136, 137, 138, 139, 140, 5, 6, 7,
121    144, 145, 22, 147, 148, 149, 150, 4, 152, 153, 154, 155, 20, 21, 158, 26,
122    32, 160, 161, 162, 163, 164, 165, 166, 167, 168, 91, 46, 60, 40, 43, 33,
123    38, 169, 170, 171, 172, 173, 174, 175, 176, 177, 93, 36, 42, 41, 59, 94,
124    45, 47, 178, 179, 180, 181, 182, 183, 184, 185, 124, 44, 37, 95, 62, 63,
125    186, 187, 188, 189, 190, 191, 192, 193, 194, 96, 58, 35, 64, 39, 61, 34,
126    195, 97, 98, 99, 100, 101, 102, 103, 104, 105, 196, 197, 198, 199, 200, 201,
127    202, 106, 107, 108, 109, 110, 111, 112, 113, 114, 203, 204, 205, 206, 207, 208,
128    209, 126, 115, 116, 117, 118, 119, 120, 121, 122, 210, 211, 212, 213, 214, 215,
129    216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231,
130    123, 65, 66, 67, 68, 69, 70, 71, 72, 73, 232, 233, 234, 235, 236, 237,
131    125, 74, 75, 76, 77, 78, 79, 80, 81, 82, 238, 239, 240, 241, 242, 243,
132    92, 159, 83, 84, 85, 86, 87, 88, 89, 90, 244, 245, 246, 247, 248, 249,
133    48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 250, 251, 252, 253, 254, 255};
134
135
136static const unsigned char a2e[256] = {
137    0, 1, 2, 3, 55, 45, 46, 47, 22, 5, 37, 11, 12, 13, 14, 15,
138    16, 17, 18, 19, 60, 61, 50, 38, 24, 25, 63, 39, 28, 29, 30, 31,
139    64, 79, 127, 123, 91, 108, 80, 125, 77, 93, 92, 78, 107, 96, 75, 97,
140    240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 122, 94, 76, 126, 110, 111,
141    124, 193, 194, 195, 196, 197, 198, 199, 200, 201, 209, 210, 211, 212, 213, 214,
142    215, 216, 217, 226, 227, 228, 229, 230, 231, 232, 233, 74, 224, 90, 95, 109,
143    121, 129, 130, 131, 132, 133, 134, 135, 136, 137, 145, 146, 147, 148, 149, 150,
144    151, 152, 153, 162, 163, 164, 165, 166, 167, 168, 169, 192, 106, 208, 161, 7,
145    32, 33, 34, 35, 36, 21, 6, 23, 40, 41, 42, 43, 44, 9, 10, 27,
146    48, 49, 26, 51, 52, 53, 54, 8, 56, 57, 58, 59, 4, 20, 62, 225,
147    65, 66, 67, 68, 69, 70, 71, 72, 73, 81, 82, 83, 84, 85, 86, 87,
148    88, 89, 98, 99, 100, 101, 102, 103, 104, 105, 112, 113, 114, 115, 116, 117,
149    118, 119, 120, 128, 138, 139, 140, 141, 142, 143, 144, 154, 155, 156, 157, 158,
150    159, 160, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183,
151    184, 185, 186, 187, 188, 189, 190, 191, 202, 203, 204, 205, 206, 207, 218, 219,
152    220, 221, 222, 223, 234, 235, 236, 237, 238, 239, 250, 251, 252, 253, 254, 255};
153
154
155static void iconv_e2a(unsigned char src[], unsigned char dst[], size_t length) {
156  size_t i;
157  for (i = 0; i < length; i++)
158    dst[i] = e2a[src[i]];
159}
160
161
162static void iconv_a2e(const char* src, unsigned char dst[], size_t length) {
163  size_t srclen;
164  size_t i;
165
166  srclen = strlen(src);
167  if (srclen > length)
168    srclen = length;
169  for (i = 0; i < srclen; i++)
170    dst[i] = a2e[src[i]];
171  /* padding the remaining part with spaces */
172  for (; i < length; i++)
173    dst[i] = a2e[' '];
174}
175
176void init_process_title_mutex_once(void) {
177  uv_mutex_init(&process_title_mutex);
178}
179
180static int get_ibmi_system_status(SSTS0200* rcvr) {
181  /* rcvrlen is input parameter 2 to QWCRSSTS */
182  unsigned int rcvrlen = sizeof(*rcvr);
183  unsigned char format[8], reset_status[10];
184
185  /* format is input parameter 3 to QWCRSSTS */
186  iconv_a2e("SSTS0200", format, sizeof(format));
187  /* reset_status is input parameter 4 */
188  iconv_a2e("*NO", reset_status, sizeof(reset_status));
189
190  /* errcode is input parameter 5 to QWCRSSTS */
191  errcode_s errcode;
192
193  /* qwcrssts_pointer is the 16-byte tagged system pointer to QWCRSSTS */
194  ILEpointer __attribute__((aligned(16))) qwcrssts_pointer;
195
196  /* qwcrssts_argv is the array of argument pointers to QWCRSSTS */
197  void* qwcrssts_argv[6];
198
199  /* Set the IBM i pointer to the QSYS/QWCRSSTS *PGM object */
200  int rc = _RSLOBJ2(&qwcrssts_pointer, RSLOBJ_TS_PGM, "QWCRSSTS", "QSYS");
201
202  if (rc != 0)
203    return rc;
204
205  /* initialize the QWCRSSTS returned info structure */
206  memset(rcvr, 0, sizeof(*rcvr));
207
208  /* initialize the QWCRSSTS error code structure */
209  memset(&errcode, 0, sizeof(errcode));
210  errcode.bytes_provided = sizeof(errcode);
211
212  /* initialize the array of argument pointers for the QWCRSSTS API */
213  qwcrssts_argv[0] = rcvr;
214  qwcrssts_argv[1] = &rcvrlen;
215  qwcrssts_argv[2] = &format;
216  qwcrssts_argv[3] = &reset_status;
217  qwcrssts_argv[4] = &errcode;
218  qwcrssts_argv[5] = NULL;
219
220  /* Call the IBM i QWCRSSTS API from PASE */
221  rc = _PGMCALL(&qwcrssts_pointer, qwcrssts_argv, 0);
222
223  return rc;
224}
225
226
227uint64_t uv_get_free_memory(void) {
228  SSTS0200 rcvr;
229
230  if (get_ibmi_system_status(&rcvr))
231    return 0;
232
233  return (uint64_t)rcvr.main_storage_size * 1024ULL;
234}
235
236
237uint64_t uv_get_total_memory(void) {
238  SSTS0200 rcvr;
239
240  if (get_ibmi_system_status(&rcvr))
241    return 0;
242
243  return (uint64_t)rcvr.main_storage_size * 1024ULL;
244}
245
246
247uint64_t uv_get_constrained_memory(void) {
248  return 0;  /* Memory constraints are unknown. */
249}
250
251
252uint64_t uv_get_available_memory(void) {
253  return uv_get_free_memory();
254}
255
256
257void uv_loadavg(double avg[3]) {
258  SSTS0200 rcvr;
259
260  if (get_ibmi_system_status(&rcvr)) {
261    avg[0] = avg[1] = avg[2] = 0;
262    return;
263  }
264
265  /* The average (in tenths) of the elapsed time during which the processing
266   * units were in use. For example, a value of 411 in binary would be 41.1%.
267   * This percentage could be greater than 100% for an uncapped partition.
268   */
269  double processing_unit_used_percent =
270    rcvr.percent_processing_unit_used / 1000.0;
271
272  avg[0] = avg[1] = avg[2] = processing_unit_used_percent;
273}
274
275
276int uv_resident_set_memory(size_t* rss) {
277  *rss = 0;
278  return 0;
279}
280
281
282int uv_uptime(double* uptime) {
283  return UV_ENOSYS;
284}
285
286
287int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
288  unsigned int numcpus, idx = 0;
289  uv_cpu_info_t* cpu_info;
290
291  *cpu_infos = NULL;
292  *count = 0;
293
294  numcpus = sysconf(_SC_NPROCESSORS_ONLN);
295
296  *cpu_infos = uv__malloc(numcpus * sizeof(uv_cpu_info_t));
297  if (!*cpu_infos) {
298    return UV_ENOMEM;
299  }
300
301  cpu_info = *cpu_infos;
302  for (idx = 0; idx < numcpus; idx++) {
303    cpu_info->speed = 0;
304    cpu_info->model = uv__strdup("unknown");
305    cpu_info->cpu_times.user = 0;
306    cpu_info->cpu_times.sys = 0;
307    cpu_info->cpu_times.idle = 0;
308    cpu_info->cpu_times.irq = 0;
309    cpu_info->cpu_times.nice = 0;
310    cpu_info++;
311  }
312  *count = numcpus;
313
314  return 0;
315}
316
317
318static int get_ibmi_physical_address(const char* line, char (*phys_addr)[6]) {
319  LIND0500 rcvr;
320  /* rcvrlen is input parameter 2 to QDCRLIND */
321  unsigned int rcvrlen = sizeof(rcvr);
322  unsigned char format[8], line_name[10];
323  unsigned char mac_addr[sizeof(rcvr.loca_adapter_address)];
324  int c[6];
325
326  /* format is input parameter 3 to QDCRLIND */
327  iconv_a2e("LIND0500", format, sizeof(format));
328
329  /* line_name is input parameter 4 to QDCRLIND */
330  iconv_a2e(line, line_name, sizeof(line_name));
331
332  /* err is input parameter 5 to QDCRLIND */
333  errcode_s err;
334
335  /* qwcrssts_pointer is the 16-byte tagged system pointer to QDCRLIND */
336  ILEpointer __attribute__((aligned(16))) qdcrlind_pointer;
337
338  /* qwcrssts_argv is the array of argument pointers to QDCRLIND */
339  void* qdcrlind_argv[6];
340
341  /* Set the IBM i pointer to the QSYS/QDCRLIND *PGM object */
342  int rc = _RSLOBJ2(&qdcrlind_pointer, RSLOBJ_TS_PGM, "QDCRLIND", "QSYS");
343
344  if (rc != 0)
345    return rc;
346
347  /* initialize the QDCRLIND returned info structure */
348  memset(&rcvr, 0, sizeof(rcvr));
349
350  /* initialize the QDCRLIND error code structure */
351  memset(&err, 0, sizeof(err));
352  err.bytes_provided = sizeof(err);
353
354  /* initialize the array of argument pointers for the QDCRLIND API */
355  qdcrlind_argv[0] = &rcvr;
356  qdcrlind_argv[1] = &rcvrlen;
357  qdcrlind_argv[2] = &format;
358  qdcrlind_argv[3] = &line_name;
359  qdcrlind_argv[4] = &err;
360  qdcrlind_argv[5] = NULL;
361
362  /* Call the IBM i QDCRLIND API from PASE */
363  rc = _PGMCALL(&qdcrlind_pointer, qdcrlind_argv, 0);
364  if (rc != 0)
365    return rc;
366
367  if (err.bytes_available > 0) {
368    return -1;
369  }
370
371  /* convert ebcdic loca_adapter_address to ascii first */
372  iconv_e2a(rcvr.loca_adapter_address, mac_addr,
373            sizeof(rcvr.loca_adapter_address));
374
375  /* convert loca_adapter_address(char[12]) to phys_addr(char[6]) */
376  int r = sscanf(mac_addr, "%02x%02x%02x%02x%02x%02x",
377                &c[0], &c[1], &c[2], &c[3], &c[4], &c[5]);
378
379  if (r == ARRAY_SIZE(c)) {
380    (*phys_addr)[0] = c[0];
381    (*phys_addr)[1] = c[1];
382    (*phys_addr)[2] = c[2];
383    (*phys_addr)[3] = c[3];
384    (*phys_addr)[4] = c[4];
385    (*phys_addr)[5] = c[5];
386  } else {
387    memset(*phys_addr, 0, sizeof(*phys_addr));
388    rc = -1;
389  }
390  return rc;
391}
392
393
394int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
395  uv_interface_address_t* address;
396  struct ifaddrs_pase *ifap = NULL, *cur;
397  int inet6, r = 0;
398
399  *count = 0;
400  *addresses = NULL;
401
402  if (Qp2getifaddrs(&ifap))
403    return UV_ENOSYS;
404
405  /* The first loop to get the size of the array to be allocated */
406  for (cur = ifap; cur; cur = cur->ifa_next) {
407    if (!(cur->ifa_addr->sa_family == AF_INET6 ||
408          cur->ifa_addr->sa_family == AF_INET))
409      continue;
410
411    if (!(cur->ifa_flags & IFF_UP && cur->ifa_flags & IFF_RUNNING))
412      continue;
413
414    (*count)++;
415  }
416
417  if (*count == 0) {
418    Qp2freeifaddrs(ifap);
419    return 0;
420  }
421
422  /* Alloc the return interface structs */
423  *addresses = uv__calloc(*count, sizeof(**addresses));
424  if (*addresses == NULL) {
425    Qp2freeifaddrs(ifap);
426    return UV_ENOMEM;
427  }
428  address = *addresses;
429
430  /* The second loop to fill in the array */
431  for (cur = ifap; cur; cur = cur->ifa_next) {
432    if (!(cur->ifa_addr->sa_family == AF_INET6 ||
433          cur->ifa_addr->sa_family == AF_INET))
434      continue;
435
436    if (!(cur->ifa_flags & IFF_UP && cur->ifa_flags & IFF_RUNNING))
437      continue;
438
439    address->name = uv__strdup(cur->ifa_name);
440
441    inet6 = (cur->ifa_addr->sa_family == AF_INET6);
442
443    if (inet6) {
444      address->address.address6 = *((struct sockaddr_in6*)cur->ifa_addr);
445      address->netmask.netmask6 = *((struct sockaddr_in6*)cur->ifa_netmask);
446      address->netmask.netmask6.sin6_family = AF_INET6;
447    } else {
448      address->address.address4 = *((struct sockaddr_in*)cur->ifa_addr);
449      address->netmask.netmask4 = *((struct sockaddr_in*)cur->ifa_netmask);
450      address->netmask.netmask4.sin_family = AF_INET;
451    }
452    address->is_internal = cur->ifa_flags & IFF_LOOPBACK ? 1 : 0;
453    if (!address->is_internal) {
454      int rc = -1;
455      size_t name_len = strlen(address->name);
456      /* To get the associated MAC address, we must convert the address to a
457       * line description. Normally, the name field contains the line
458       * description name, but for VLANs it has the VLAN appended with a
459       * period. Since object names can also contain periods and numbers, there
460       * is no way to know if a returned name is for a VLAN or not. eg.
461       * *LIND ETH1.1 and *LIND ETH1, VLAN 1 both have the same name: ETH1.1
462       *
463       * Instead, we apply the same heuristic used by some of the XPF ioctls:
464       * - names > 10 *must* contain a VLAN
465       * - assume names <= 10 do not contain a VLAN and try directly
466       * - if >10 or QDCRLIND returned an error, try to strip off a VLAN
467       *   and try again
468       * - if we still get an error or couldn't find a period, leave the MAC as
469       *   00:00:00:00:00:00
470       */
471      if (name_len <= 10) {
472        /* Assume name does not contain a VLAN ID */
473        rc = get_ibmi_physical_address(address->name, &address->phys_addr);
474      }
475
476      if (name_len > 10 || rc != 0) {
477        /* The interface name must contain a VLAN ID suffix. Attempt to strip
478         * it off so we can get the line description to pass to QDCRLIND.
479         */
480        char* temp_name = uv__strdup(address->name);
481        char* dot = strrchr(temp_name, '.');
482        if (dot != NULL) {
483          *dot = '\0';
484          if (strlen(temp_name) <= 10) {
485            rc = get_ibmi_physical_address(temp_name, &address->phys_addr);
486          }
487        }
488        uv__free(temp_name);
489      }
490    }
491
492    address++;
493  }
494
495  Qp2freeifaddrs(ifap);
496  return r;
497}
498
499
500void uv_free_interface_addresses(uv_interface_address_t* addresses, int count) {
501  int i;
502
503  for (i = 0; i < count; ++i) {
504    uv__free(addresses[i].name);
505  }
506
507  uv__free(addresses);
508}
509
510char** uv_setup_args(int argc, char** argv) {
511  char exepath[UV__PATH_MAX];
512  char* s;
513  size_t size;
514
515  if (argc > 0) {
516    /* Use argv[0] to determine value for uv_exepath(). */
517    size = sizeof(exepath);
518    if (uv__search_path(argv[0], exepath, &size) == 0) {
519      uv_once(&process_title_mutex_once, init_process_title_mutex_once);
520      uv_mutex_lock(&process_title_mutex);
521      original_exepath = uv__strdup(exepath);
522      uv_mutex_unlock(&process_title_mutex);
523    }
524  }
525
526  return argv;
527}
528
529int uv_set_process_title(const char* title) {
530  return 0;
531}
532
533int uv_get_process_title(char* buffer, size_t size) {
534  if (buffer == NULL || size == 0)
535    return UV_EINVAL;
536
537  buffer[0] = '\0';
538  return 0;
539}
540
541void uv__process_title_cleanup(void) {
542}
543