nscd.c revision 194086
1158115Sume/*- 2158115Sume * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru> 3158115Sume * All rights reserved. 4158115Sume * 5158115Sume * Redistribution and use in source and binary forms, with or without 6158115Sume * modification, are permitted provided that the following conditions 7158115Sume * are met: 8158115Sume * 1. Redistributions of source code must retain the above copyright 9158115Sume * notice, this list of conditions and the following disclaimer. 10158115Sume * 2. Redistributions in binary form must reproduce the above copyright 11158115Sume * notice, this list of conditions and the following disclaimer in thereg 12158115Sume * documentation and/or other materials provided with the distribution. 13158115Sume * 14158115Sume * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15158115Sume * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16158115Sume * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17158115Sume * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18158115Sume * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19158115Sume * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20158115Sume * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21158115Sume * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22158115Sume * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23158115Sume * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24158115Sume * SUCH DAMAGE. 25158115Sume * 26158115Sume */ 27158115Sume 28158115Sume#include <sys/cdefs.h> 29158115Sume__FBSDID("$FreeBSD: head/usr.sbin/nscd/nscd.c 194086 2009-06-12 23:27:31Z des $"); 30158115Sume 31158115Sume#include <sys/types.h> 32158115Sume#include <sys/event.h> 33158115Sume#include <sys/socket.h> 34158115Sume#include <sys/time.h> 35158115Sume#include <sys/param.h> 36158115Sume#include <sys/un.h> 37158115Sume#include <assert.h> 38158115Sume#include <err.h> 39158115Sume#include <errno.h> 40158115Sume#include <fcntl.h> 41158115Sume#include <libutil.h> 42158115Sume#include <pthread.h> 43158115Sume#include <signal.h> 44158115Sume#include <stdio.h> 45158115Sume#include <stdlib.h> 46158115Sume#include <string.h> 47158115Sume#include <unistd.h> 48158115Sume 49158115Sume#include "agents/passwd.h" 50158115Sume#include "agents/group.h" 51158115Sume#include "agents/services.h" 52158115Sume#include "cachelib.h" 53158115Sume#include "config.h" 54158115Sume#include "debug.h" 55158115Sume#include "log.h" 56171795Sbushman#include "nscdcli.h" 57158115Sume#include "parser.h" 58158115Sume#include "query.h" 59158115Sume#include "singletons.h" 60158115Sume 61158115Sume#ifndef CONFIG_PATH 62171795Sbushman#define CONFIG_PATH "/etc/nscd.conf" 63158115Sume#endif 64171795Sbushman#define DEFAULT_CONFIG_PATH "nscd.conf" 65158115Sume 66158115Sume#define MAX_SOCKET_IO_SIZE 4096 67158115Sume 68158115Sumestruct processing_thread_args { 69158115Sume cache the_cache; 70158115Sume struct configuration *the_configuration; 71158115Sume struct runtime_env *the_runtime_env; 72158115Sume}; 73158115Sume 74158115Sumestatic void accept_connection(struct kevent *, struct runtime_env *, 75158115Sume struct configuration *); 76158115Sumestatic void destroy_cache_(cache); 77158115Sumestatic void destroy_runtime_env(struct runtime_env *); 78158115Sumestatic cache init_cache_(struct configuration *); 79158115Sumestatic struct runtime_env *init_runtime_env(struct configuration *); 80158115Sumestatic void processing_loop(cache, struct runtime_env *, 81158115Sume struct configuration *); 82158115Sumestatic void process_socket_event(struct kevent *, struct runtime_env *, 83158115Sume struct configuration *); 84158115Sumestatic void process_timer_event(struct kevent *, struct runtime_env *, 85158115Sume struct configuration *); 86158115Sumestatic void *processing_thread(void *); 87158115Sumestatic void usage(void); 88158115Sume 89158115Sumevoid get_time_func(struct timeval *); 90158115Sume 91158115Sumestatic void 92158115Sumeusage(void) 93158115Sume{ 94162891Sru fprintf(stderr, 95171795Sbushman "usage: nscd [-dnst] [-i cachename] [-I cachename]\n"); 96158115Sume exit(1); 97158115Sume} 98158115Sume 99158115Sumestatic cache 100158115Sumeinit_cache_(struct configuration *config) 101158115Sume{ 102158115Sume struct cache_params params; 103158115Sume cache retval; 104158115Sume 105158115Sume struct configuration_entry *config_entry; 106158115Sume size_t size, i; 107158115Sume int res; 108158115Sume 109158115Sume TRACE_IN(init_cache_); 110158115Sume 111158115Sume memset(¶ms, 0, sizeof(struct cache_params)); 112158115Sume params.get_time_func = get_time_func; 113158115Sume retval = init_cache(¶ms); 114158115Sume 115158115Sume size = configuration_get_entries_size(config); 116158115Sume for (i = 0; i < size; ++i) { 117158115Sume config_entry = configuration_get_entry(config, i); 118158115Sume /* 119158115Sume * We should register common entries now - multipart entries 120158115Sume * would be registered automatically during the queries. 121158115Sume */ 122158115Sume res = register_cache_entry(retval, (struct cache_entry_params *) 123158115Sume &config_entry->positive_cache_params); 124158115Sume config_entry->positive_cache_entry = find_cache_entry(retval, 125158115Sume config_entry->positive_cache_params.entry_name); 126158115Sume assert(config_entry->positive_cache_entry != 127158115Sume INVALID_CACHE_ENTRY); 128158115Sume 129158115Sume res = register_cache_entry(retval, (struct cache_entry_params *) 130158115Sume &config_entry->negative_cache_params); 131158115Sume config_entry->negative_cache_entry = find_cache_entry(retval, 132158115Sume config_entry->negative_cache_params.entry_name); 133158115Sume assert(config_entry->negative_cache_entry != 134158115Sume INVALID_CACHE_ENTRY); 135158115Sume } 136158115Sume 137158115Sume LOG_MSG_2("cache", "cache was successfully initialized"); 138158115Sume TRACE_OUT(init_cache_); 139158115Sume return (retval); 140158115Sume} 141158115Sume 142158115Sumestatic void 143158115Sumedestroy_cache_(cache the_cache) 144158115Sume{ 145158115Sume TRACE_IN(destroy_cache_); 146158115Sume destroy_cache(the_cache); 147158115Sume TRACE_OUT(destroy_cache_); 148158115Sume} 149158115Sume 150158115Sume/* 151158115Sume * Socket and kqueues are prepared here. We have one global queue for both 152158115Sume * socket and timers events. 153158115Sume */ 154158115Sumestatic struct runtime_env * 155158115Sumeinit_runtime_env(struct configuration *config) 156158115Sume{ 157158115Sume int serv_addr_len; 158158115Sume struct sockaddr_un serv_addr; 159158115Sume 160158115Sume struct kevent eventlist; 161158115Sume struct timespec timeout; 162158115Sume 163158115Sume struct runtime_env *retval; 164158115Sume 165158115Sume TRACE_IN(init_runtime_env); 166183770Sdelphij retval = (struct runtime_env *)calloc(1, sizeof(struct runtime_env)); 167158115Sume assert(retval != NULL); 168158115Sume 169158115Sume retval->sockfd = socket(PF_LOCAL, SOCK_STREAM, 0); 170158115Sume 171158115Sume if (config->force_unlink == 1) 172158115Sume unlink(config->socket_path); 173158115Sume 174158115Sume memset(&serv_addr, 0, sizeof(struct sockaddr_un)); 175158115Sume serv_addr.sun_family = PF_LOCAL; 176184187Sdelphij strlcpy(serv_addr.sun_path, config->socket_path, 177158115Sume sizeof(serv_addr.sun_path)); 178158115Sume serv_addr_len = sizeof(serv_addr.sun_family) + 179158115Sume strlen(serv_addr.sun_path) + 1; 180158115Sume 181158115Sume if (bind(retval->sockfd, (struct sockaddr *)&serv_addr, 182158115Sume serv_addr_len) == -1) { 183158115Sume close(retval->sockfd); 184158115Sume free(retval); 185158115Sume 186158115Sume LOG_ERR_2("runtime environment", "can't bind socket to path: " 187158115Sume "%s", config->socket_path); 188158115Sume TRACE_OUT(init_runtime_env); 189158115Sume return (NULL); 190158115Sume } 191158115Sume LOG_MSG_2("runtime environment", "using socket %s", 192158115Sume config->socket_path); 193158115Sume 194158115Sume /* 195158115Sume * Here we're marking socket as non-blocking and setting its backlog 196158115Sume * to the maximum value 197158115Sume */ 198158115Sume chmod(config->socket_path, config->socket_mode); 199158115Sume listen(retval->sockfd, -1); 200158115Sume fcntl(retval->sockfd, F_SETFL, O_NONBLOCK); 201158115Sume 202158115Sume retval->queue = kqueue(); 203158115Sume assert(retval->queue != -1); 204158115Sume 205158115Sume EV_SET(&eventlist, retval->sockfd, EVFILT_READ, EV_ADD | EV_ONESHOT, 206158115Sume 0, 0, 0); 207158115Sume memset(&timeout, 0, sizeof(struct timespec)); 208158115Sume kevent(retval->queue, &eventlist, 1, NULL, 0, &timeout); 209158115Sume 210158115Sume LOG_MSG_2("runtime environment", "successfully initialized"); 211158115Sume TRACE_OUT(init_runtime_env); 212158115Sume return (retval); 213158115Sume} 214158115Sume 215158115Sumestatic void 216158115Sumedestroy_runtime_env(struct runtime_env *env) 217158115Sume{ 218158115Sume TRACE_IN(destroy_runtime_env); 219158115Sume close(env->queue); 220158115Sume close(env->sockfd); 221158115Sume free(env); 222158115Sume TRACE_OUT(destroy_runtime_env); 223158115Sume} 224158115Sume 225158115Sumestatic void 226158115Sumeaccept_connection(struct kevent *event_data, struct runtime_env *env, 227158115Sume struct configuration *config) 228158115Sume{ 229158115Sume struct kevent eventlist[2]; 230158115Sume struct timespec timeout; 231158115Sume struct query_state *qstate; 232158115Sume 233158115Sume int fd; 234158115Sume int res; 235158115Sume 236158115Sume uid_t euid; 237158115Sume gid_t egid; 238158115Sume 239158115Sume TRACE_IN(accept_connection); 240158115Sume fd = accept(event_data->ident, NULL, NULL); 241158115Sume if (fd == -1) { 242158115Sume LOG_ERR_2("accept_connection", "error %d during accept()", 243158115Sume errno); 244158115Sume TRACE_OUT(accept_connection); 245158115Sume return; 246158115Sume } 247158115Sume 248158115Sume if (getpeereid(fd, &euid, &egid) != 0) { 249158115Sume LOG_ERR_2("accept_connection", "error %d during getpeereid()", 250158115Sume errno); 251158115Sume TRACE_OUT(accept_connection); 252158115Sume return; 253158115Sume } 254158115Sume 255158115Sume qstate = init_query_state(fd, sizeof(int), euid, egid); 256158115Sume if (qstate == NULL) { 257158115Sume LOG_ERR_2("accept_connection", "can't init query_state"); 258158115Sume TRACE_OUT(accept_connection); 259158115Sume return; 260158115Sume } 261158115Sume 262158115Sume memset(&timeout, 0, sizeof(struct timespec)); 263158115Sume EV_SET(&eventlist[0], fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 264158115Sume 0, qstate->timeout.tv_sec * 1000, qstate); 265158115Sume EV_SET(&eventlist[1], fd, EVFILT_READ, EV_ADD | EV_ONESHOT, 266158115Sume NOTE_LOWAT, qstate->kevent_watermark, qstate); 267158115Sume res = kevent(env->queue, eventlist, 2, NULL, 0, &timeout); 268158115Sume if (res < 0) 269158115Sume LOG_ERR_2("accept_connection", "kevent error"); 270158115Sume 271158115Sume TRACE_OUT(accept_connection); 272158115Sume} 273158115Sume 274158115Sumestatic void 275158115Sumeprocess_socket_event(struct kevent *event_data, struct runtime_env *env, 276158115Sume struct configuration *config) 277158115Sume{ 278158115Sume struct kevent eventlist[2]; 279158115Sume struct timeval query_timeout; 280158115Sume struct timespec kevent_timeout; 281158115Sume int nevents; 282158115Sume int eof_res, res; 283158115Sume ssize_t io_res; 284158115Sume struct query_state *qstate; 285158115Sume 286158115Sume TRACE_IN(process_socket_event); 287158115Sume eof_res = event_data->flags & EV_EOF ? 1 : 0; 288158115Sume res = 0; 289158115Sume 290158115Sume memset(&kevent_timeout, 0, sizeof(struct timespec)); 291158115Sume EV_SET(&eventlist[0], event_data->ident, EVFILT_TIMER, EV_DELETE, 292158115Sume 0, 0, NULL); 293158115Sume nevents = kevent(env->queue, eventlist, 1, NULL, 0, &kevent_timeout); 294158115Sume if (nevents == -1) { 295158115Sume if (errno == ENOENT) { 296158115Sume /* the timer is already handling this event */ 297158115Sume TRACE_OUT(process_socket_event); 298158115Sume return; 299158115Sume } else { 300158115Sume /* some other error happened */ 301158115Sume LOG_ERR_2("process_socket_event", "kevent error, errno" 302158115Sume " is %d", errno); 303158115Sume TRACE_OUT(process_socket_event); 304158115Sume return; 305158115Sume } 306158115Sume } 307158115Sume qstate = (struct query_state *)event_data->udata; 308158115Sume 309158115Sume /* 310158115Sume * If the buffer that is to be send/received is too large, 311158115Sume * we send it implicitly, by using query_io_buffer_read and 312158115Sume * query_io_buffer_write functions in the query_state. These functions 313158115Sume * use the temporary buffer, which is later send/received in parts. 314158115Sume * The code below implements buffer splitting/mergind for send/receive 315158115Sume * operations. It also does the actual socket IO operations. 316158115Sume */ 317158115Sume if (((qstate->use_alternate_io == 0) && 318158115Sume (qstate->kevent_watermark <= event_data->data)) || 319158115Sume ((qstate->use_alternate_io != 0) && 320158115Sume (qstate->io_buffer_watermark <= event_data->data))) { 321158115Sume if (qstate->use_alternate_io != 0) { 322158115Sume switch (qstate->io_buffer_filter) { 323158115Sume case EVFILT_READ: 324158115Sume io_res = query_socket_read(qstate, 325158115Sume qstate->io_buffer_p, 326158115Sume qstate->io_buffer_watermark); 327158115Sume if (io_res < 0) { 328158115Sume qstate->use_alternate_io = 0; 329158115Sume qstate->process_func = NULL; 330158115Sume } else { 331158115Sume qstate->io_buffer_p += io_res; 332158115Sume if (qstate->io_buffer_p == 333158115Sume qstate->io_buffer + 334158115Sume qstate->io_buffer_size) { 335158115Sume qstate->io_buffer_p = 336158115Sume qstate->io_buffer; 337158115Sume qstate->use_alternate_io = 0; 338158115Sume } 339158115Sume } 340158115Sume break; 341158115Sume default: 342158115Sume break; 343158115Sume } 344158115Sume } 345158115Sume 346158115Sume if (qstate->use_alternate_io == 0) { 347158115Sume do { 348158115Sume res = qstate->process_func(qstate); 349158115Sume } while ((qstate->kevent_watermark == 0) && 350158115Sume (qstate->process_func != NULL) && 351158115Sume (res == 0)); 352158115Sume 353158115Sume if (res != 0) 354158115Sume qstate->process_func = NULL; 355158115Sume } 356158115Sume 357158115Sume if ((qstate->use_alternate_io != 0) && 358158115Sume (qstate->io_buffer_filter == EVFILT_WRITE)) { 359158115Sume io_res = query_socket_write(qstate, qstate->io_buffer_p, 360158115Sume qstate->io_buffer_watermark); 361158115Sume if (io_res < 0) { 362158115Sume qstate->use_alternate_io = 0; 363158115Sume qstate->process_func = NULL; 364158115Sume } else 365158115Sume qstate->io_buffer_p += io_res; 366158115Sume } 367158115Sume } else { 368158115Sume /* assuming that socket was closed */ 369158115Sume qstate->process_func = NULL; 370158115Sume qstate->use_alternate_io = 0; 371158115Sume } 372158115Sume 373158115Sume if (((qstate->process_func == NULL) && 374158115Sume (qstate->use_alternate_io == 0)) || 375158115Sume (eof_res != 0) || (res != 0)) { 376158115Sume destroy_query_state(qstate); 377158115Sume close(event_data->ident); 378158115Sume TRACE_OUT(process_socket_event); 379158115Sume return; 380158115Sume } 381158115Sume 382158115Sume /* updating the query_state lifetime variable */ 383158115Sume get_time_func(&query_timeout); 384158115Sume query_timeout.tv_usec = 0; 385158115Sume query_timeout.tv_sec -= qstate->creation_time.tv_sec; 386158115Sume if (query_timeout.tv_sec > qstate->timeout.tv_sec) 387158115Sume query_timeout.tv_sec = 0; 388158115Sume else 389158115Sume query_timeout.tv_sec = qstate->timeout.tv_sec - 390158115Sume query_timeout.tv_sec; 391158115Sume 392158115Sume if ((qstate->use_alternate_io != 0) && (qstate->io_buffer_p == 393158115Sume qstate->io_buffer + qstate->io_buffer_size)) 394158115Sume qstate->use_alternate_io = 0; 395158115Sume 396158115Sume if (qstate->use_alternate_io == 0) { 397158115Sume /* 398158115Sume * If we must send/receive the large block of data, 399158115Sume * we should prepare the query_state's io_XXX fields. 400158115Sume * We should also substitute its write_func and read_func 401158115Sume * with the query_io_buffer_write and query_io_buffer_read, 402158115Sume * which will allow us to implicitly send/receive this large 403158115Sume * buffer later (in the subsequent calls to the 404158115Sume * process_socket_event). 405158115Sume */ 406158115Sume if (qstate->kevent_watermark > MAX_SOCKET_IO_SIZE) { 407158115Sume if (qstate->io_buffer != NULL) 408158115Sume free(qstate->io_buffer); 409158115Sume 410183770Sdelphij qstate->io_buffer = (char *)calloc(1, 411158115Sume qstate->kevent_watermark); 412158115Sume assert(qstate->io_buffer != NULL); 413158115Sume 414158115Sume qstate->io_buffer_p = qstate->io_buffer; 415158115Sume qstate->io_buffer_size = qstate->kevent_watermark; 416158115Sume qstate->io_buffer_filter = qstate->kevent_filter; 417158115Sume 418158115Sume qstate->write_func = query_io_buffer_write; 419158115Sume qstate->read_func = query_io_buffer_read; 420158115Sume 421158115Sume if (qstate->kevent_filter == EVFILT_READ) 422158115Sume qstate->use_alternate_io = 1; 423158115Sume 424158115Sume qstate->io_buffer_watermark = MAX_SOCKET_IO_SIZE; 425158115Sume EV_SET(&eventlist[1], event_data->ident, 426158115Sume qstate->kevent_filter, EV_ADD | EV_ONESHOT, 427158115Sume NOTE_LOWAT, MAX_SOCKET_IO_SIZE, qstate); 428158115Sume } else { 429158115Sume EV_SET(&eventlist[1], event_data->ident, 430158115Sume qstate->kevent_filter, EV_ADD | EV_ONESHOT, 431158115Sume NOTE_LOWAT, qstate->kevent_watermark, qstate); 432158115Sume } 433158115Sume } else { 434158115Sume if (qstate->io_buffer + qstate->io_buffer_size - 435158115Sume qstate->io_buffer_p < 436158115Sume MAX_SOCKET_IO_SIZE) { 437158115Sume qstate->io_buffer_watermark = qstate->io_buffer + 438158115Sume qstate->io_buffer_size - qstate->io_buffer_p; 439158115Sume EV_SET(&eventlist[1], event_data->ident, 440158115Sume qstate->io_buffer_filter, 441158115Sume EV_ADD | EV_ONESHOT, NOTE_LOWAT, 442158115Sume qstate->io_buffer_watermark, 443158115Sume qstate); 444158115Sume } else { 445158115Sume qstate->io_buffer_watermark = MAX_SOCKET_IO_SIZE; 446158115Sume EV_SET(&eventlist[1], event_data->ident, 447158115Sume qstate->io_buffer_filter, EV_ADD | EV_ONESHOT, 448158115Sume NOTE_LOWAT, MAX_SOCKET_IO_SIZE, qstate); 449158115Sume } 450158115Sume } 451158115Sume EV_SET(&eventlist[0], event_data->ident, EVFILT_TIMER, 452158115Sume EV_ADD | EV_ONESHOT, 0, query_timeout.tv_sec * 1000, qstate); 453158115Sume kevent(env->queue, eventlist, 2, NULL, 0, &kevent_timeout); 454158115Sume 455158115Sume TRACE_OUT(process_socket_event); 456158115Sume} 457158115Sume 458158115Sume/* 459158115Sume * This routine is called if timer event has been signaled in the kqueue. It 460158115Sume * just closes the socket and destroys the query_state. 461158115Sume */ 462158115Sumestatic void 463158115Sumeprocess_timer_event(struct kevent *event_data, struct runtime_env *env, 464158115Sume struct configuration *config) 465158115Sume{ 466158115Sume struct query_state *qstate; 467158115Sume 468158115Sume TRACE_IN(process_timer_event); 469158115Sume qstate = (struct query_state *)event_data->udata; 470158115Sume destroy_query_state(qstate); 471158115Sume close(event_data->ident); 472158115Sume TRACE_OUT(process_timer_event); 473158115Sume} 474158115Sume 475158115Sume/* 476158115Sume * Processing loop is the basic processing routine, that forms a body of each 477158115Sume * procssing thread 478158115Sume */ 479158115Sumestatic void 480158115Sumeprocessing_loop(cache the_cache, struct runtime_env *env, 481158115Sume struct configuration *config) 482158115Sume{ 483158115Sume struct timespec timeout; 484158115Sume const int eventlist_size = 1; 485158115Sume struct kevent eventlist[eventlist_size]; 486158115Sume int nevents, i; 487158115Sume 488158115Sume TRACE_MSG("=> processing_loop"); 489158115Sume memset(&timeout, 0, sizeof(struct timespec)); 490158115Sume memset(&eventlist, 0, sizeof(struct kevent) * eventlist_size); 491158115Sume 492158115Sume for (;;) { 493158115Sume nevents = kevent(env->queue, NULL, 0, eventlist, 494158115Sume eventlist_size, NULL); 495158115Sume /* 496158115Sume * we can only receive 1 event on success 497158115Sume */ 498158115Sume if (nevents == 1) { 499158115Sume struct kevent *event_data; 500158115Sume event_data = &eventlist[0]; 501158115Sume 502158115Sume if (event_data->ident == env->sockfd) { 503158115Sume for (i = 0; i < event_data->data; ++i) 504158115Sume accept_connection(event_data, env, config); 505158115Sume 506158115Sume EV_SET(eventlist, s_runtime_env->sockfd, 507158115Sume EVFILT_READ, EV_ADD | EV_ONESHOT, 508158115Sume 0, 0, 0); 509158115Sume memset(&timeout, 0, 510158115Sume sizeof(struct timespec)); 511158115Sume kevent(s_runtime_env->queue, eventlist, 512158115Sume 1, NULL, 0, &timeout); 513158115Sume 514158115Sume } else { 515158115Sume switch (event_data->filter) { 516158115Sume case EVFILT_READ: 517158115Sume case EVFILT_WRITE: 518158115Sume process_socket_event(event_data, 519158115Sume env, config); 520158115Sume break; 521158115Sume case EVFILT_TIMER: 522158115Sume process_timer_event(event_data, 523158115Sume env, config); 524158115Sume break; 525158115Sume default: 526158115Sume break; 527158115Sume } 528158115Sume } 529158115Sume } else { 530158115Sume /* this branch shouldn't be currently executed */ 531158115Sume } 532158115Sume } 533158115Sume 534158115Sume TRACE_MSG("<= processing_loop"); 535158115Sume} 536158115Sume 537158115Sume/* 538158115Sume * Wrapper above the processing loop function. It sets the thread signal mask 539158115Sume * to avoid SIGPIPE signals (which can happen if the client works incorrectly). 540158115Sume */ 541158115Sumestatic void * 542158115Sumeprocessing_thread(void *data) 543158115Sume{ 544158115Sume struct processing_thread_args *args; 545158115Sume sigset_t new; 546158115Sume 547158115Sume TRACE_MSG("=> processing_thread"); 548158115Sume args = (struct processing_thread_args *)data; 549158115Sume 550158115Sume sigemptyset(&new); 551158115Sume sigaddset(&new, SIGPIPE); 552158115Sume if (pthread_sigmask(SIG_BLOCK, &new, NULL) != 0) 553158115Sume LOG_ERR_1("processing thread", 554158115Sume "thread can't block the SIGPIPE signal"); 555158115Sume 556158115Sume processing_loop(args->the_cache, args->the_runtime_env, 557158115Sume args->the_configuration); 558158115Sume free(args); 559158115Sume TRACE_MSG("<= processing_thread"); 560158115Sume 561158115Sume return (NULL); 562158115Sume} 563158115Sume 564158115Sumevoid 565158115Sumeget_time_func(struct timeval *time) 566158115Sume{ 567158115Sume struct timespec res; 568158115Sume memset(&res, 0, sizeof(struct timespec)); 569158115Sume clock_gettime(CLOCK_MONOTONIC, &res); 570158115Sume 571158115Sume time->tv_sec = res.tv_sec; 572158115Sume time->tv_usec = 0; 573158115Sume} 574158115Sume 575158115Sume/* 576194086Sdes * The idea of _nss_cache_cycle_prevention_function is that nsdispatch 577194086Sdes * will search for this symbol in the executable. This symbol is the 578194086Sdes * attribute of the caching daemon. So, if it exists, nsdispatch won't try 579194086Sdes * to connect to the caching daemon and will just ignore the 'cache' 580194086Sdes * source in the nsswitch.conf. This method helps to avoid cycles and 581194086Sdes * organize self-performing requests. 582194086Sdes * 583194086Sdes * (not actually a function; it used to be, but it doesn't make any 584194086Sdes * difference, as long as it has external linkage) 585158115Sume */ 586194086Sdesvoid *_nss_cache_cycle_prevention_function; 587158115Sume 588158115Sumeint 589158115Sumemain(int argc, char *argv[]) 590158115Sume{ 591158115Sume struct processing_thread_args *thread_args; 592158115Sume pthread_t *threads; 593158115Sume 594158115Sume struct pidfh *pidfile; 595158115Sume pid_t pid; 596158115Sume 597158115Sume char const *config_file; 598158115Sume char const *error_str; 599158115Sume int error_line; 600158115Sume int i, res; 601158115Sume 602158115Sume int trace_mode_enabled; 603158115Sume int force_single_threaded; 604158115Sume int do_not_daemonize; 605158115Sume int clear_user_cache_entries, clear_all_cache_entries; 606158115Sume char *user_config_entry_name, *global_config_entry_name; 607158115Sume int show_statistics; 608158115Sume int daemon_mode, interactive_mode; 609158115Sume 610158115Sume 611158115Sume /* by default all debug messages are omitted */ 612158115Sume TRACE_OFF(); 613158115Sume 614158115Sume /* parsing command line arguments */ 615158115Sume trace_mode_enabled = 0; 616158115Sume force_single_threaded = 0; 617158115Sume do_not_daemonize = 0; 618158115Sume clear_user_cache_entries = 0; 619158115Sume clear_all_cache_entries = 0; 620158115Sume show_statistics = 0; 621158115Sume user_config_entry_name = NULL; 622158115Sume global_config_entry_name = NULL; 623158115Sume while ((res = getopt(argc, argv, "nstdi:I:")) != -1) { 624158115Sume switch (res) { 625158115Sume case 'n': 626158115Sume do_not_daemonize = 1; 627158115Sume break; 628158115Sume case 's': 629158115Sume force_single_threaded = 1; 630158115Sume break; 631158115Sume case 't': 632158115Sume trace_mode_enabled = 1; 633158115Sume break; 634158115Sume case 'i': 635158115Sume clear_user_cache_entries = 1; 636158115Sume if (optarg != NULL) 637158115Sume if (strcmp(optarg, "all") != 0) 638158115Sume user_config_entry_name = strdup(optarg); 639158115Sume break; 640158115Sume case 'I': 641158115Sume clear_all_cache_entries = 1; 642158115Sume if (optarg != NULL) 643158115Sume if (strcmp(optarg, "all") != 0) 644158115Sume global_config_entry_name = 645158115Sume strdup(optarg); 646158115Sume break; 647158115Sume case 'd': 648158115Sume show_statistics = 1; 649158115Sume break; 650158115Sume case '?': 651158115Sume default: 652158115Sume usage(); 653158115Sume /* NOT REACHED */ 654158115Sume } 655158115Sume } 656158115Sume 657158115Sume daemon_mode = do_not_daemonize | force_single_threaded | 658158115Sume trace_mode_enabled; 659158115Sume interactive_mode = clear_user_cache_entries | clear_all_cache_entries | 660158115Sume show_statistics; 661158115Sume 662158115Sume if ((daemon_mode != 0) && (interactive_mode != 0)) { 663158115Sume LOG_ERR_1("main", "daemon mode and interactive_mode arguments " 664158115Sume "can't be used together"); 665158115Sume usage(); 666158115Sume } 667158115Sume 668158115Sume if (interactive_mode != 0) { 669158115Sume FILE *pidfin = fopen(DEFAULT_PIDFILE_PATH, "r"); 670158115Sume char pidbuf[256]; 671158115Sume 672171795Sbushman struct nscd_connection_params connection_params; 673171795Sbushman nscd_connection connection; 674158115Sume 675158115Sume int result; 676158115Sume 677158115Sume if (pidfin == NULL) 678158115Sume errx(EXIT_FAILURE, "There is no daemon running."); 679158115Sume 680158115Sume memset(pidbuf, 0, sizeof(pidbuf)); 681158115Sume fread(pidbuf, sizeof(pidbuf) - 1, 1, pidfin); 682158115Sume fclose(pidfin); 683158115Sume 684158115Sume if (ferror(pidfin) != 0) 685158115Sume errx(EXIT_FAILURE, "Can't read from pidfile."); 686158115Sume 687158115Sume if (sscanf(pidbuf, "%d", &pid) != 1) 688158115Sume errx(EXIT_FAILURE, "Invalid pidfile."); 689158115Sume LOG_MSG_1("main", "daemon PID is %d", pid); 690158115Sume 691158115Sume 692158115Sume memset(&connection_params, 0, 693171795Sbushman sizeof(struct nscd_connection_params)); 694158115Sume connection_params.socket_path = DEFAULT_SOCKET_PATH; 695171795Sbushman connection = open_nscd_connection__(&connection_params); 696171795Sbushman if (connection == INVALID_NSCD_CONNECTION) 697158115Sume errx(EXIT_FAILURE, "Can't connect to the daemon."); 698158115Sume 699158115Sume if (clear_user_cache_entries != 0) { 700171795Sbushman result = nscd_transform__(connection, 701158115Sume user_config_entry_name, TT_USER); 702158115Sume if (result != 0) 703158115Sume LOG_MSG_1("main", 704158115Sume "user cache transformation failed"); 705158115Sume else 706158115Sume LOG_MSG_1("main", 707158115Sume "user cache_transformation " 708158115Sume "succeeded"); 709158115Sume } 710158115Sume 711158115Sume if (clear_all_cache_entries != 0) { 712158115Sume if (geteuid() != 0) 713158115Sume errx(EXIT_FAILURE, "Only root can initiate " 714158115Sume "global cache transformation."); 715158115Sume 716171795Sbushman result = nscd_transform__(connection, 717158115Sume global_config_entry_name, TT_ALL); 718158115Sume if (result != 0) 719158115Sume LOG_MSG_1("main", 720158115Sume "global cache transformation " 721158115Sume "failed"); 722158115Sume else 723158115Sume LOG_MSG_1("main", 724158115Sume "global cache transformation " 725158115Sume "succeeded"); 726158115Sume } 727158115Sume 728171795Sbushman close_nscd_connection__(connection); 729158115Sume 730158115Sume free(user_config_entry_name); 731158115Sume free(global_config_entry_name); 732158115Sume return (EXIT_SUCCESS); 733158115Sume } 734158115Sume 735158115Sume pidfile = pidfile_open(DEFAULT_PIDFILE_PATH, 0644, &pid); 736158115Sume if (pidfile == NULL) { 737158115Sume if (errno == EEXIST) 738158115Sume errx(EXIT_FAILURE, "Daemon already running, pid: %d.", 739158115Sume pid); 740158115Sume warn("Cannot open or create pidfile"); 741158115Sume } 742158115Sume 743158115Sume if (trace_mode_enabled == 1) 744158115Sume TRACE_ON(); 745158115Sume 746158115Sume /* blocking the main thread from receiving SIGPIPE signal */ 747158115Sume sigblock(sigmask(SIGPIPE)); 748158115Sume 749158115Sume /* daemonization */ 750158115Sume if (do_not_daemonize == 0) { 751158115Sume res = daemon(0, trace_mode_enabled == 0 ? 0 : 1); 752158115Sume if (res != 0) { 753158115Sume LOG_ERR_1("main", "can't daemonize myself: %s", 754158115Sume strerror(errno)); 755158115Sume pidfile_remove(pidfile); 756158115Sume goto fin; 757158115Sume } else 758158115Sume LOG_MSG_1("main", "successfully daemonized"); 759158115Sume } 760158115Sume 761158115Sume pidfile_write(pidfile); 762158115Sume 763158115Sume s_agent_table = init_agent_table(); 764158115Sume register_agent(s_agent_table, init_passwd_agent()); 765158115Sume register_agent(s_agent_table, init_passwd_mp_agent()); 766158115Sume register_agent(s_agent_table, init_group_agent()); 767158115Sume register_agent(s_agent_table, init_group_mp_agent()); 768158115Sume register_agent(s_agent_table, init_services_agent()); 769158115Sume register_agent(s_agent_table, init_services_mp_agent()); 770158115Sume LOG_MSG_1("main", "request agents registered successfully"); 771158115Sume 772158115Sume /* 773158115Sume * Hosts agent can't work properly until we have access to the 774158115Sume * appropriate dtab structures, which are used in nsdispatch 775158115Sume * calls 776158115Sume * 777158115Sume register_agent(s_agent_table, init_hosts_agent()); 778158115Sume */ 779158115Sume 780158115Sume /* configuration initialization */ 781158115Sume s_configuration = init_configuration(); 782158115Sume fill_configuration_defaults(s_configuration); 783158115Sume 784158115Sume error_str = NULL; 785158115Sume error_line = 0; 786158115Sume config_file = CONFIG_PATH; 787158115Sume 788158115Sume res = parse_config_file(s_configuration, config_file, &error_str, 789158115Sume &error_line); 790158115Sume if ((res != 0) && (error_str == NULL)) { 791158115Sume config_file = DEFAULT_CONFIG_PATH; 792158115Sume res = parse_config_file(s_configuration, config_file, 793158115Sume &error_str, &error_line); 794158115Sume } 795158115Sume 796158115Sume if (res != 0) { 797158115Sume if (error_str != NULL) { 798158115Sume LOG_ERR_1("main", "error in configuration file(%s, %d): %s\n", 799158115Sume config_file, error_line, error_str); 800158115Sume } else { 801158115Sume LOG_ERR_1("main", "no configuration file found " 802158115Sume "- was looking for %s and %s", 803158115Sume CONFIG_PATH, DEFAULT_CONFIG_PATH); 804158115Sume } 805158115Sume destroy_configuration(s_configuration); 806158115Sume return (-1); 807158115Sume } 808158115Sume 809158115Sume if (force_single_threaded == 1) 810158115Sume s_configuration->threads_num = 1; 811158115Sume 812158115Sume /* cache initialization */ 813158115Sume s_cache = init_cache_(s_configuration); 814158115Sume if (s_cache == NULL) { 815158115Sume LOG_ERR_1("main", "can't initialize the cache"); 816158115Sume destroy_configuration(s_configuration); 817158115Sume return (-1); 818158115Sume } 819158115Sume 820158115Sume /* runtime environment initialization */ 821158115Sume s_runtime_env = init_runtime_env(s_configuration); 822158115Sume if (s_runtime_env == NULL) { 823158115Sume LOG_ERR_1("main", "can't initialize the runtime environment"); 824158115Sume destroy_configuration(s_configuration); 825158115Sume destroy_cache_(s_cache); 826158115Sume return (-1); 827158115Sume } 828158115Sume 829158115Sume if (s_configuration->threads_num > 1) { 830183770Sdelphij threads = (pthread_t *)calloc(1, sizeof(pthread_t) * 831158115Sume s_configuration->threads_num); 832158115Sume for (i = 0; i < s_configuration->threads_num; ++i) { 833158115Sume thread_args = (struct processing_thread_args *)malloc( 834158115Sume sizeof(struct processing_thread_args)); 835158115Sume thread_args->the_cache = s_cache; 836158115Sume thread_args->the_runtime_env = s_runtime_env; 837158115Sume thread_args->the_configuration = s_configuration; 838158115Sume 839158115Sume LOG_MSG_1("main", "thread #%d was successfully created", 840158115Sume i); 841158115Sume pthread_create(&threads[i], NULL, processing_thread, 842158115Sume thread_args); 843158115Sume 844158115Sume thread_args = NULL; 845158115Sume } 846158115Sume 847158115Sume for (i = 0; i < s_configuration->threads_num; ++i) 848158115Sume pthread_join(threads[i], NULL); 849158115Sume } else { 850158115Sume LOG_MSG_1("main", "working in single-threaded mode"); 851158115Sume processing_loop(s_cache, s_runtime_env, s_configuration); 852158115Sume } 853158115Sume 854158115Sumefin: 855158115Sume /* runtime environment destruction */ 856158115Sume destroy_runtime_env(s_runtime_env); 857158115Sume 858158115Sume /* cache destruction */ 859158115Sume destroy_cache_(s_cache); 860158115Sume 861158115Sume /* configuration destruction */ 862158115Sume destroy_configuration(s_configuration); 863158115Sume 864158115Sume /* agents table destruction */ 865158115Sume destroy_agent_table(s_agent_table); 866158115Sume 867158115Sume pidfile_remove(pidfile); 868158115Sume return (EXIT_SUCCESS); 869158115Sume} 870