1/* 2 * 3 * Copyright (c) International Business Machines Corp., 2009 4 * Copyright (c) 2014 Oracle and/or its affiliates. All Rights Reserved. 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 14 * the GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 */ 20 21#include <ctype.h> 22#include <errno.h> 23#include <fcntl.h> 24#include <limits.h> 25#include <stdio.h> 26#include <stdlib.h> 27#include <sys/types.h> 28#include <unistd.h> 29#include "test.h" 30#include "tst_pid.h" 31#include "old_safe_file_ops.h" 32#include "tst_safe_macros.h" 33#include "lapi/syscalls.h" 34 35#define PID_MAX_PATH "/proc/sys/kernel/pid_max" 36#define THREADS_MAX_PATH "/proc/sys/kernel/threads-max" 37#define CGROUPS_V1_SLICE_FMT "/sys/fs/cgroup/pids/user.slice/user-%d.slice/pids.max" 38#define CGROUPS_V2_SLICE_FMT "/sys/fs/cgroup/user.slice/user-%d.slice/pids.max" 39/* Leave some available processes for the OS */ 40#define PIDS_RESERVE 50 41 42pid_t tst_get_unused_pid_(void (*cleanup_fn) (void)) 43{ 44 pid_t pid; 45 46 SAFE_FILE_SCANF(cleanup_fn, PID_MAX_PATH, "%d", &pid); 47 48 return pid; 49} 50 51/* 52 * Get the effective session UID - either one invoking current test via sudo 53 * or the real UID. 54 */ 55static unsigned int get_session_uid(void) 56{ 57 const char *sudo_uid; 58 59 sudo_uid = getenv("SUDO_UID"); 60 if (sudo_uid) { 61 unsigned int real_uid; 62 int ret; 63 64 ret = sscanf(sudo_uid, "%u", &real_uid); 65 if (ret == 1) 66 return real_uid; 67 } 68 69 return getuid(); 70} 71 72static int read_session_pids_limit(const char *path_fmt, int uid, 73 void (*cleanup_fn) (void)) 74{ 75 int max_pids, ret; 76 char max_pid_value[100]; 77 char path[PATH_MAX]; 78 79 ret = snprintf(path, sizeof(path), path_fmt, uid); 80 if (ret < 0 || (size_t)ret >= sizeof(path)) 81 return -1; 82 83 if (access(path, R_OK) != 0) { 84 tst_resm(TINFO, "Cannot read session user limits from '%s'", path); 85 return -1; 86 } 87 88 SAFE_FILE_SCANF(cleanup_fn, path, "%s", max_pid_value); 89 if (!strcmp(max_pid_value, "max")) { 90 SAFE_FILE_SCANF(cleanup_fn, PID_MAX_PATH, "%d", &max_pids); 91 tst_resm(TINFO, "Found limit of processes %d (from %s=max)", max_pids, path); 92 } else { 93 max_pids = SAFE_STRTOL(max_pid_value, 0, INT_MAX); 94 tst_resm(TINFO, "Found limit of processes %d (from %s)", max_pids, path); 95 } 96 97 return max_pids; 98} 99 100static int get_session_pids_limit(void (*cleanup_fn) (void)) 101{ 102 int max_pids, uid; 103 104 uid = get_session_uid(); 105 max_pids = read_session_pids_limit(CGROUPS_V2_SLICE_FMT, uid, cleanup_fn); 106 if (max_pids < 0) 107 max_pids = read_session_pids_limit(CGROUPS_V1_SLICE_FMT, uid, 108 cleanup_fn); 109 110 if (max_pids < 0) 111 return -1; 112 113 return max_pids; 114} 115 116static int get_used_pids(void (*cleanup_fn) (void)) 117{ 118 DIR *dir_proc; 119 struct dirent *ent; 120 char status_path[PATH_MAX]; 121 int used_threads, used_pids = 0; 122 123 dir_proc = SAFE_OPENDIR("/proc"); 124 125 while ((ent = SAFE_READDIR(dir_proc))) { 126 if (isdigit(ent->d_name[0])) { 127 snprintf(status_path, sizeof(status_path), "/proc/%s/status", ent->d_name); 128 if (!FILE_LINES_SCANF(cleanup_fn, status_path, "Threads: %d", &used_threads)) 129 used_pids += used_threads; 130 } 131 } 132 133 SAFE_CLOSEDIR(dir_proc); 134 135 return used_pids; 136} 137 138int tst_get_free_pids_(void (*cleanup_fn) (void)) 139{ 140 int max_pids, max_session_pids, max_threads, used_pids = get_used_pids(cleanup_fn); 141 142 SAFE_FILE_SCANF(cleanup_fn, PID_MAX_PATH, "%d", &max_pids); 143 SAFE_FILE_SCANF(cleanup_fn, THREADS_MAX_PATH, "%d", &max_threads); 144 max_pids = MIN(max_pids, max_threads); 145 146 max_session_pids = get_session_pids_limit(cleanup_fn); 147 if ((max_session_pids > 0) && (max_session_pids < max_pids)) 148 max_pids = max_session_pids; 149 150 if (max_pids > PIDS_RESERVE) 151 max_pids -= PIDS_RESERVE; 152 else 153 max_pids = 0; 154 155 /* max_pids contains the maximum PID + 1, 156 * used_pids contains used PIDs + 1, 157 * so this additional '1' is eliminated by the substraction */ 158 if (used_pids >= max_pids) { 159 tst_brkm(TBROK, cleanup_fn, "No free pids"); 160 return 0; 161 } 162 return max_pids - used_pids; 163} 164 165pid_t tst_getpid(void) 166{ 167 return syscall(SYS_getpid); 168} 169