1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * This file contains a set of routines used to perform wait based method 30 * reaping. 31 */ 32 33 #include <wait.h> 34 #include <sys/param.h> 35 #include <fcntl.h> 36 #include <libcontract.h> 37 #include <errno.h> 38 #include <libintl.h> 39 #include <unistd.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <sys/resource.h> 43 #include "inetd_impl.h" 44 45 /* inetd's open file limit, set in method_init() */ 46 #define INETD_NOFILE_LIMIT RLIM_INFINITY 47 48 /* structure used to represent an active method process */ 49 typedef struct { 50 int fd; /* fd of process's /proc psinfo file */ 51 /* associated contract id if known, else -1 */ 52 ctid_t cid; 53 pid_t pid; 54 instance_t *inst; /* pointer to associated instance */ 55 instance_method_t method; /* the method type running */ 56 uu_list_node_t link; 57 } method_el_t; 58 59 60 static void unregister_method(method_el_t *); 61 62 63 /* list of currently executing method processes */ 64 static uu_list_pool_t *method_pool = NULL; 65 static uu_list_t *method_list = NULL; 66 67 /* 68 * File limit saved during initialization before modification, so that it can 69 * be reverted back to for inetd's exec'd methods. 70 */ 71 static struct rlimit saved_file_limit; 72 73 /* 74 * Setup structures used for method termination monitoring. 75 * Returns -1 if an allocation failure occurred, else 0. 76 */ 77 int 78 method_init(void) 79 { 80 struct rlimit rl; 81 82 /* 83 * Save aside the old file limit and impose one large enough to support 84 * all the /proc file handles we could have open. 85 */ 86 87 (void) getrlimit(RLIMIT_NOFILE, &saved_file_limit); 88 89 rl.rlim_cur = rl.rlim_max = INETD_NOFILE_LIMIT; 90 if (setrlimit(RLIMIT_NOFILE, &rl) == -1) { 91 error_msg("Failed to set file limit: %s", strerror(errno)); 92 return (-1); 93 } 94 95 if ((method_pool = uu_list_pool_create("method_pool", 96 sizeof (method_el_t), offsetof(method_el_t, link), NULL, 97 UU_LIST_POOL_DEBUG)) == NULL) { 98 error_msg("%s: %s", gettext("Failed to create method pool"), 99 uu_strerror(uu_error())); 100 return (-1); 101 } 102 103 if ((method_list = uu_list_create(method_pool, NULL, 0)) == NULL) { 104 error_msg("%s: %s", 105 gettext("Failed to create method list"), 106 uu_strerror(uu_error())); 107 /* let method_fini() clean-up */ 108 return (-1); 109 } 110 111 return (0); 112 } 113 114 /* 115 * Tear-down structures created in method_init(). 116 */ 117 void 118 method_fini(void) 119 { 120 if (method_list != NULL) { 121 method_el_t *me; 122 123 while ((me = uu_list_first(method_list)) != NULL) 124 unregister_method(me); 125 126 (void) uu_list_destroy(method_list); 127 method_list = NULL; 128 } 129 if (method_pool != NULL) { 130 (void) uu_list_pool_destroy(method_pool); 131 method_pool = NULL; 132 } 133 134 /* revert file limit */ 135 method_preexec(); 136 } 137 138 /* 139 * Revert file limit back to pre-initialization one. This shouldn't fail as 140 * long as its called *after* descriptor cleanup. 141 */ 142 void 143 method_preexec(void) 144 { 145 (void) setrlimit(RLIMIT_NOFILE, &saved_file_limit); 146 } 147 148 149 /* 150 * Callback function that handles the timeout of an instance's method. 151 * 'arg' points at the method_el_t representing the method. 152 */ 153 /* ARGSUSED0 */ 154 static void 155 method_timeout(iu_tq_t *tq, void *arg) 156 { 157 method_el_t *mp = arg; 158 159 error_msg(gettext("The %s method of instance %s timed-out"), 160 methods[mp->method].name, mp->inst->fmri); 161 162 mp->inst->timer_id = -1; 163 164 if (mp->method == IM_START) { 165 process_start_term(mp->inst); 166 } else { 167 process_non_start_term(mp->inst, IMRET_FAILURE); 168 } 169 170 unregister_method(mp); 171 } 172 173 /* 174 * Registers the attributes of a running method passed as arguments so that 175 * the method's termination is noticed and any further processing of the 176 * associated instance is carried out. The function also sets up any 177 * necessary timers so we can detect hung methods. 178 * Returns -1 if either it failed to open the /proc psinfo file which is used 179 * to monitor the method process, it failed to setup a required timer or 180 * memory allocation failed; else 0. 181 */ 182 int 183 register_method(instance_t *ins, pid_t pid, ctid_t cid, instance_method_t mthd) 184 { 185 char path[MAXPATHLEN]; 186 int fd; 187 method_el_t *me; 188 189 /* open /proc psinfo file of process to listen for POLLHUP events on */ 190 (void) snprintf(path, sizeof (path), "/proc/%u/psinfo", pid); 191 for (;;) { 192 if ((fd = open(path, O_RDONLY)) >= 0) { 193 break; 194 } else if (errno != EINTR) { 195 /* 196 * Don't output an error for ENOENT; we get this 197 * if a method has gone away whilst we were stopped, 198 * and we're now trying to re-listen for it. 199 */ 200 if (errno != ENOENT) { 201 error_msg(gettext("Failed to open %s: %s"), 202 path, strerror(errno)); 203 } 204 return (-1); 205 } 206 } 207 208 /* add method record to in-memory list */ 209 if ((me = calloc(1, sizeof (method_el_t))) == NULL) { 210 error_msg(strerror(errno)); 211 (void) close(fd); 212 return (-1); 213 } 214 me->fd = fd; 215 me->inst = (instance_t *)ins; 216 me->method = mthd; 217 me->pid = pid; 218 me->cid = cid; 219 220 /* register a timeout for the method, if required */ 221 if (mthd != IM_START) { 222 method_info_t *mi = ins->config->methods[mthd]; 223 224 if (mi->timeout > 0) { 225 assert(ins->timer_id == -1); 226 ins->timer_id = iu_schedule_timer(timer_queue, 227 mi->timeout, method_timeout, me); 228 if (ins->timer_id == -1) { 229 error_msg(gettext( 230 "Failed to schedule method timeout")); 231 free(me); 232 (void) close(fd); 233 return (-1); 234 } 235 } 236 } 237 238 /* 239 * Add fd of psinfo file to poll set, but pass 0 for events to 240 * poll for, so we should only get a POLLHUP event on the fd. 241 */ 242 if (set_pollfd(fd, 0) == -1) { 243 cancel_inst_timer(ins); 244 free(me); 245 (void) close(fd); 246 return (-1); 247 } 248 249 uu_list_node_init(me, &me->link, method_pool); 250 (void) uu_list_insert_after(method_list, NULL, me); 251 252 return (0); 253 } 254 255 /* 256 * A counterpart to register_method(), this function stops the monitoring of a 257 * method process for its termination. 258 */ 259 static void 260 unregister_method(method_el_t *me) 261 { 262 /* cancel any timer associated with the method */ 263 if (me->inst->timer_id != -1) 264 cancel_inst_timer(me->inst); 265 266 /* stop polling on the psinfo file fd */ 267 clear_pollfd(me->fd); 268 (void) close(me->fd); 269 270 /* remove method record from list */ 271 uu_list_remove(method_list, me); 272 273 free(me); 274 } 275 276 /* 277 * Unregister all methods associated with instance 'inst'. 278 */ 279 void 280 unregister_instance_methods(const instance_t *inst) 281 { 282 method_el_t *me = uu_list_first(method_list); 283 284 while (me != NULL) { 285 if (me->inst == inst) { 286 method_el_t *tmp = me; 287 288 me = uu_list_next(method_list, me); 289 unregister_method(tmp); 290 } else { 291 me = uu_list_next(method_list, me); 292 } 293 } 294 } 295 296 /* 297 * Process any terminated methods. For each method determined to have 298 * terminated, the function determines its return value and calls the 299 * appropriate handling function, depending on the type of the method. 300 */ 301 void 302 process_terminated_methods(void) 303 { 304 method_el_t *me = uu_list_first(method_list); 305 306 while (me != NULL) { 307 struct pollfd *pfd; 308 pid_t pid; 309 int status; 310 int ret; 311 method_el_t *tmp; 312 313 pfd = find_pollfd(me->fd); 314 315 /* 316 * We expect to get a POLLHUP back on the fd of the process's 317 * open psinfo file from /proc when the method terminates. 318 * A POLLERR could(?) mask a POLLHUP, so handle this 319 * also. 320 */ 321 if ((pfd->revents & (POLLHUP|POLLERR)) == 0) { 322 me = uu_list_next(method_list, me); 323 continue; 324 } 325 326 /* get the method's exit code (no need to loop for EINTR) */ 327 pid = waitpid(me->pid, &status, WNOHANG); 328 329 switch (pid) { 330 case 0: /* child still around */ 331 /* 332 * Either poll() is sending us invalid POLLHUP events 333 * or is flagging a POLLERR on the fd. Neither should 334 * happen, but in the event they do, ignore this fd 335 * this time around and wait out the termination 336 * of its associated method. This may result in 337 * inetd swiftly looping in event_loop(), but means 338 * we don't miss the termination of a method. 339 */ 340 me = uu_list_next(method_list, me); 341 continue; 342 343 case -1: /* non-existent child */ 344 assert(errno == ECHILD); 345 /* 346 * the method must not be owned by inetd due to it 347 * persisting over an inetd restart. Let's assume the 348 * best, that it was successful. 349 */ 350 ret = IMRET_SUCCESS; 351 break; 352 353 default: /* child terminated */ 354 if (WIFEXITED(status)) { 355 ret = WEXITSTATUS(status); 356 debug_msg("process %d of instance %s returned " 357 "%d", pid, me->inst->fmri, ret); 358 } else if (WIFSIGNALED(status)) { 359 /* 360 * Terminated by signal. This may be due 361 * to a kill that we sent from a disable or 362 * offline event. We flag it as a failure, but 363 * this flagged failure will only be processed 364 * in the case of non-start methods, or when 365 * the instance is still enabled. 366 */ 367 debug_msg("process %d of instance %s exited " 368 "due to signal %d", pid, me->inst->fmri, 369 WTERMSIG(status)); 370 ret = IMRET_FAILURE; 371 } else { 372 /* 373 * Can we actually get here? Don't think so. 374 * Treat it as a failure, anyway. 375 */ 376 debug_msg("waitpid() for %s method of " 377 "instance %s returned %d", 378 methods[me->method].name, me->inst->fmri, 379 status); 380 ret = IMRET_FAILURE; 381 } 382 } 383 384 remove_method_ids(me->inst, me->pid, me->cid, me->method); 385 386 /* continue state transition processing of the instance */ 387 if (me->method != IM_START) { 388 process_non_start_term(me->inst, ret); 389 } else { 390 process_start_term(me->inst); 391 } 392 393 if (me->cid != -1) 394 (void) abandon_contract(me->cid); 395 396 tmp = me; 397 me = uu_list_next(method_list, me); 398 unregister_method(tmp); 399 } 400 } 401