libuv/src/unix/linux/linux-core.c
Fedor Indutny dc97d44c56 unix: move uv_set_process_title() to proctitle.c
Use hijacking argv array to change process' title. It seems to be working fine
on almost every platform (at least it should not break anything as it's used in
nginx in a similar way).
2012-07-13 14:57:40 +02:00

403 lines
11 KiB
C

/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "uv.h"
#include "../internal.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <net/if.h>
#include <sys/param.h>
#include <sys/sysinfo.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
#define HAVE_IFADDRS_H 1
#ifdef __UCLIBC__
# if __UCLIBC_MAJOR__ < 0 || __UCLIBC_MINOR__ < 9 || __UCLIBC_SUBLEVEL__ < 32
# undef HAVE_IFADDRS_H
# endif
#endif
#ifdef HAVE_IFADDRS_H
# include <ifaddrs.h>
#endif
#undef NANOSEC
#define NANOSEC ((uint64_t) 1e9)
/* This is rather annoying: CLOCK_BOOTTIME lives in <linux/time.h> but we can't
* include that file because it conflicts with <time.h>. We'll just have to
* define it ourselves.
*/
#ifndef CLOCK_BOOTTIME
# define CLOCK_BOOTTIME 7
#endif
static char buf[MAXPATHLEN + 1];
/*
* There's probably some way to get time from Linux than gettimeofday(). What
* it is, I don't know.
*/
uint64_t uv_hrtime() {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (ts.tv_sec * NANOSEC + ts.tv_nsec);
}
void uv_loadavg(double avg[3]) {
struct sysinfo info;
if (sysinfo(&info) < 0) return;
avg[0] = (double) info.loads[0] / 65536.0;
avg[1] = (double) info.loads[1] / 65536.0;
avg[2] = (double) info.loads[2] / 65536.0;
}
int uv_exepath(char* buffer, size_t* size) {
ssize_t n;
if (!buffer || !size) {
return -1;
}
n = readlink("/proc/self/exe", buffer, *size - 1);
if (n <= 0) return -1;
buffer[n] = '\0';
*size = n;
return 0;
}
uint64_t uv_get_free_memory(void) {
return (uint64_t) sysconf(_SC_PAGESIZE) * sysconf(_SC_AVPHYS_PAGES);
}
uint64_t uv_get_total_memory(void) {
return (uint64_t) sysconf(_SC_PAGESIZE) * sysconf(_SC_PHYS_PAGES);
}
uv_err_t uv_resident_set_memory(size_t* rss) {
FILE* f;
int itmp;
char ctmp;
unsigned int utmp;
size_t page_size = getpagesize();
char *cbuf;
int foundExeEnd;
f = fopen("/proc/self/stat", "r");
if (!f) return uv__new_sys_error(errno);
/* PID */
if (fscanf(f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
/* Exec file */
cbuf = buf;
foundExeEnd = 0;
if (fscanf (f, "%c", cbuf++) == 0) goto error;
while (1) {
if (fscanf(f, "%c", cbuf) == 0) goto error;
if (*cbuf == ')') {
foundExeEnd = 1;
} else if (foundExeEnd && *cbuf == ' ') {
*cbuf = 0;
break;
}
cbuf++;
}
/* State */
if (fscanf (f, "%c ", &ctmp) == 0) goto error; /* coverity[secure_coding] */
/* Parent process */
if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
/* Process group */
if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
/* Session id */
if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
/* TTY */
if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
/* TTY owner process group */
if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
/* Flags */
if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */
/* Minor faults (no memory page) */
if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */
/* Minor faults, children */
if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */
/* Major faults (memory page faults) */
if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */
/* Major faults, children */
if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */
/* utime */
if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
/* stime */
if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
/* utime, children */
if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
/* stime, children */
if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
/* jiffies remaining in current time slice */
if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
/* 'nice' value */
if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
/* jiffies until next timeout */
if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */
/* jiffies until next SIGALRM */
if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */
/* start time (jiffies since system boot) */
if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */
/* Virtual memory size */
if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */
/* Resident set size */
if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */
*rss = (size_t) utmp * page_size;
/* rlim */
if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */
/* Start of text */
if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */
/* End of text */
if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */
/* Start of stack */
if (fscanf (f, "%u ", &utmp) == 0) goto error; /* coverity[secure_coding] */
fclose (f);
return uv_ok_;
error:
fclose (f);
return uv__new_sys_error(errno);
}
uv_err_t uv_uptime(double* uptime) {
static volatile int no_clock_boottime;
struct timespec now;
int r;
/* Try CLOCK_BOOTTIME first, fall back to CLOCK_MONOTONIC if not available
* (pre-2.6.39 kernels). CLOCK_MONOTONIC doesn't increase when the system
* is suspended.
*/
if (no_clock_boottime) {
retry: r = clock_gettime(CLOCK_MONOTONIC, &now);
}
else if ((r = clock_gettime(CLOCK_BOOTTIME, &now)) && errno == EINVAL) {
no_clock_boottime = 1;
goto retry;
}
if (r)
return uv__new_sys_error(errno);
*uptime = now.tv_sec;
*uptime += (double)now.tv_nsec / 1000000000.0;
return uv_ok_;
}
uv_err_t uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK),
multiplier = ((uint64_t)1000L / ticks), cpuspeed;
int numcpus = 0, i = 0;
unsigned long ticks_user, ticks_sys, ticks_idle, ticks_nice, ticks_intr;
char line[512], speedPath[256], model[512];
FILE *fpStat = fopen("/proc/stat", "r");
FILE *fpModel = fopen("/proc/cpuinfo", "r");
FILE *fpSpeed;
uv_cpu_info_t* cpu_info;
if (fpModel) {
while (fgets(line, 511, fpModel) != NULL) {
if (strncmp(line, "model name", 10) == 0) {
numcpus++;
if (numcpus == 1) {
char *p = strchr(line, ':') + 2;
strcpy(model, p);
model[strlen(model)-1] = 0;
}
} else if (strncmp(line, "cpu MHz", 7) == 0) {
if (numcpus == 1) {
sscanf(line, "%*s %*s : %u", &cpuspeed);
}
}
}
fclose(fpModel);
}
*cpu_infos = (uv_cpu_info_t*)malloc(numcpus * sizeof(uv_cpu_info_t));
if (!(*cpu_infos)) {
return uv__new_artificial_error(UV_ENOMEM);
}
*count = numcpus;
cpu_info = *cpu_infos;
if (fpStat) {
while (fgets(line, 511, fpStat) != NULL) {
if (strncmp(line, "cpu ", 4) == 0) {
continue;
} else if (strncmp(line, "cpu", 3) != 0) {
break;
}
sscanf(line, "%*s %lu %lu %lu %lu %*s %lu",
&ticks_user, &ticks_nice, &ticks_sys, &ticks_idle, &ticks_intr);
snprintf(speedPath, sizeof(speedPath),
"/sys/devices/system/cpu/cpu%u/cpufreq/cpuinfo_max_freq", i);
fpSpeed = fopen(speedPath, "r");
if (fpSpeed) {
if (fgets(line, 511, fpSpeed) != NULL) {
sscanf(line, "%u", &cpuspeed);
cpuspeed /= 1000;
}
fclose(fpSpeed);
}
cpu_info->cpu_times.user = ticks_user * multiplier;
cpu_info->cpu_times.nice = ticks_nice * multiplier;
cpu_info->cpu_times.sys = ticks_sys * multiplier;
cpu_info->cpu_times.idle = ticks_idle * multiplier;
cpu_info->cpu_times.irq = ticks_intr * multiplier;
cpu_info->model = strdup(model);
cpu_info->speed = cpuspeed;
cpu_info++;
}
fclose(fpStat);
}
return uv_ok_;
}
void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) {
int i;
for (i = 0; i < count; i++) {
free(cpu_infos[i].model);
}
free(cpu_infos);
}
uv_err_t uv_interface_addresses(uv_interface_address_t** addresses,
int* count) {
#ifndef HAVE_IFADDRS_H
return uv__new_artificial_error(UV_ENOSYS);
#else
struct ifaddrs *addrs, *ent;
char ip[INET6_ADDRSTRLEN];
uv_interface_address_t* address;
if (getifaddrs(&addrs) != 0) {
return uv__new_sys_error(errno);
}
*count = 0;
/* Count the number of interfaces */
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
if (!(ent->ifa_flags & IFF_UP && ent->ifa_flags & IFF_RUNNING) ||
(ent->ifa_addr == NULL) ||
(ent->ifa_addr->sa_family == PF_PACKET)) {
continue;
}
(*count)++;
}
*addresses = (uv_interface_address_t*)
malloc(*count * sizeof(uv_interface_address_t));
if (!(*addresses)) {
return uv__new_artificial_error(UV_ENOMEM);
}
address = *addresses;
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
bzero(&ip, sizeof (ip));
if (!(ent->ifa_flags & IFF_UP && ent->ifa_flags & IFF_RUNNING)) {
continue;
}
if (ent->ifa_addr == NULL) {
continue;
}
/*
* On Linux getifaddrs returns information related to the raw underlying
* devices. We're not interested in this information.
*/
if (ent->ifa_addr->sa_family == PF_PACKET) {
continue;
}
address->name = strdup(ent->ifa_name);
if (ent->ifa_addr->sa_family == AF_INET6) {
address->address.address6 = *((struct sockaddr_in6 *)ent->ifa_addr);
} else {
address->address.address4 = *((struct sockaddr_in *)ent->ifa_addr);
}
address->is_internal = ent->ifa_flags & IFF_LOOPBACK ? 1 : 0;
address++;
}
freeifaddrs(addrs);
return uv_ok_;
#endif
}
void uv_free_interface_addresses(uv_interface_address_t* addresses,
int count) {
int i;
for (i = 0; i < count; i++) {
free(addresses[i].name);
}
free(addresses);
}