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