nscd.c revision 183770
1178479Sjb/*- 2178479Sjb * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru> 3178479Sjb * All rights reserved. 4178479Sjb * 5178479Sjb * Redistribution and use in source and binary forms, with or without 6178479Sjb * modification, are permitted provided that the following conditions 7178479Sjb * are met: 8178479Sjb * 1. Redistributions of source code must retain the above copyright 9178479Sjb * notice, this list of conditions and the following disclaimer. 10178479Sjb * 2. Redistributions in binary form must reproduce the above copyright 11178479Sjb * notice, this list of conditions and the following disclaimer in thereg 12178479Sjb * documentation and/or other materials provided with the distribution. 13178479Sjb * 14178479Sjb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15178479Sjb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16178479Sjb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17178479Sjb * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18178479Sjb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19178479Sjb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20178479Sjb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21210767Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22178479Sjb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23210767Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24178479Sjb * SUCH DAMAGE. 25268578Srpaulo * 26268578Srpaulo */ 27297129Spfg 28268578Srpaulo#include <sys/cdefs.h> 29178479Sjb__FBSDID("$FreeBSD: head/usr.sbin/nscd/nscd.c 183770 2008-10-12 00:44:27Z delphij $"); 30178479Sjb 31297077Smav#include <sys/types.h> 32178479Sjb#include <sys/event.h> 33178479Sjb#include <sys/socket.h> 34178479Sjb#include <sys/time.h> 35178479Sjb#include <sys/param.h> 36178479Sjb#include <sys/un.h> 37178479Sjb#include <assert.h> 38178559Sjb#include <err.h> 39178559Sjb#include <errno.h> 40178559Sjb#include <fcntl.h> 41178559Sjb#include <libutil.h> 42178559Sjb#include <pthread.h> 43178479Sjb#include <signal.h> 44178479Sjb#include <stdio.h> 45297077Smav#include <stdlib.h> 46178479Sjb#include <string.h> 47178559Sjb#include <unistd.h> 48178479Sjb 49178479Sjb#include "agents/passwd.h" 50178479Sjb#include "agents/group.h" 51178479Sjb#include "agents/services.h" 52178479Sjb#include "cachelib.h" 53178479Sjb#include "config.h" 54178479Sjb#include "debug.h" 55297077Smav#include "log.h" 56178559Sjb#include "nscdcli.h" 57268578Srpaulo#include "parser.h" 58178559Sjb#include "query.h" 59178479Sjb#include "singletons.h" 60178479Sjb 61178479Sjb#ifndef CONFIG_PATH 62178479Sjb#define CONFIG_PATH "/etc/nscd.conf" 63178479Sjb#endif 64178479Sjb#define DEFAULT_CONFIG_PATH "nscd.conf" 65178479Sjb 66178479Sjb#define MAX_SOCKET_IO_SIZE 4096 67178479Sjb 68178479Sjbstruct processing_thread_args { 69178479Sjb cache the_cache; 70178479Sjb struct configuration *the_configuration; 71178479Sjb struct runtime_env *the_runtime_env; 72178479Sjb}; 73178479Sjb 74178479Sjbstatic void accept_connection(struct kevent *, struct runtime_env *, 75178479Sjb struct configuration *); 76178479Sjbstatic void destroy_cache_(cache); 77178479Sjbstatic void destroy_runtime_env(struct runtime_env *); 78178479Sjbstatic cache init_cache_(struct configuration *); 79178479Sjbstatic struct runtime_env *init_runtime_env(struct configuration *); 80178479Sjbstatic void processing_loop(cache, struct runtime_env *, 81178479Sjb struct configuration *); 82178479Sjbstatic void process_socket_event(struct kevent *, struct runtime_env *, 83210767Srpaulo struct configuration *); 84210767Srpaulostatic void process_timer_event(struct kevent *, struct runtime_env *, 85210767Srpaulo struct configuration *); 86210767Srpaulostatic void *processing_thread(void *); 87178559Sjbstatic void usage(void); 88178479Sjb 89178479Sjbvoid get_time_func(struct timeval *); 90178479Sjb 91178479Sjbstatic void 92178479Sjbusage(void) 93210425Savg{ 94210425Savg fprintf(stderr, 95210425Savg "usage: nscd [-dnst] [-i cachename] [-I cachename]\n"); 96210425Savg exit(1); 97210425Savg} 98210425Savg 99210425Savgstatic cache 100210425Savginit_cache_(struct configuration *config) 101178479Sjb{ 102178479Sjb struct cache_params params; 103178479Sjb cache retval; 104178479Sjb 105178479Sjb struct configuration_entry *config_entry; 106178479Sjb size_t size, i; 107178479Sjb int res; 108178479Sjb 109178479Sjb TRACE_IN(init_cache_); 110178479Sjb 111178479Sjb memset(¶ms, 0, sizeof(struct cache_params)); 112178559Sjb params.get_time_func = get_time_func; 113178479Sjb retval = init_cache(¶ms); 114178479Sjb 115210425Savg size = configuration_get_entries_size(config); 116178559Sjb for (i = 0; i < size; ++i) { 117210425Savg config_entry = configuration_get_entry(config, i); 118210425Savg /* 119210425Savg * We should register common entries now - multipart entries 120210425Savg * would be registered automatically during the queries. 121178559Sjb */ 122178559Sjb res = register_cache_entry(retval, (struct cache_entry_params *) 123178559Sjb &config_entry->positive_cache_params); 124178479Sjb config_entry->positive_cache_entry = find_cache_entry(retval, 125178479Sjb config_entry->positive_cache_params.entry_name); 126178479Sjb assert(config_entry->positive_cache_entry != 127178479Sjb INVALID_CACHE_ENTRY); 128178479Sjb 129178479Sjb res = register_cache_entry(retval, (struct cache_entry_params *) 130178479Sjb &config_entry->negative_cache_params); 131178479Sjb config_entry->negative_cache_entry = find_cache_entry(retval, 132178479Sjb config_entry->negative_cache_params.entry_name); 133210767Srpaulo assert(config_entry->negative_cache_entry != 134210767Srpaulo INVALID_CACHE_ENTRY); 135210767Srpaulo } 136210767Srpaulo 137178559Sjb LOG_MSG_2("cache", "cache was successfully initialized"); 138178479Sjb TRACE_OUT(init_cache_); 139178479Sjb return (retval); 140178479Sjb} 141178479Sjb 142178479Sjbstatic void 143210425Savgdestroy_cache_(cache the_cache) 144210425Savg{ 145210425Savg TRACE_IN(destroy_cache_); 146210425Savg destroy_cache(the_cache); 147210425Savg TRACE_OUT(destroy_cache_); 148210425Savg} 149210425Savg 150210425Savg/* 151178479Sjb * Socket and kqueues are prepared here. We have one global queue for both 152178479Sjb * socket and timers events. 153178479Sjb */ 154178479Sjbstatic struct runtime_env * 155178479Sjbinit_runtime_env(struct configuration *config) 156178479Sjb{ 157178479Sjb int serv_addr_len; 158178479Sjb struct sockaddr_un serv_addr; 159178479Sjb 160178479Sjb struct kevent eventlist; 161178479Sjb struct timespec timeout; 162178559Sjb 163178479Sjb struct runtime_env *retval; 164210425Savg 165178559Sjb TRACE_IN(init_runtime_env); 166210425Savg retval = (struct runtime_env *)calloc(1, sizeof(struct runtime_env)); 167210425Savg assert(retval != NULL); 168210425Savg 169210425Savg retval->sockfd = socket(PF_LOCAL, SOCK_STREAM, 0); 170178559Sjb 171178559Sjb if (config->force_unlink == 1) 172178559Sjb unlink(config->socket_path); 173178479Sjb 174178479Sjb memset(&serv_addr, 0, sizeof(struct sockaddr_un)); 175178479Sjb serv_addr.sun_family = PF_LOCAL; 176178479Sjb strncpy(serv_addr.sun_path, config->socket_path, 177178479Sjb sizeof(serv_addr.sun_path)); 178178479Sjb serv_addr_len = sizeof(serv_addr.sun_family) + 179178479Sjb strlen(serv_addr.sun_path) + 1; 180178479Sjb 181178479Sjb if (bind(retval->sockfd, (struct sockaddr *)&serv_addr, 182178479Sjb serv_addr_len) == -1) { 183178479Sjb close(retval->sockfd); 184178479Sjb free(retval); 185178479Sjb 186178479Sjb LOG_ERR_2("runtime environment", "can't bind socket to path: " 187178479Sjb "%s", config->socket_path); 188178479Sjb TRACE_OUT(init_runtime_env); 189178479Sjb return (NULL); 190178479Sjb } 191178479Sjb LOG_MSG_2("runtime environment", "using socket %s", 192178479Sjb config->socket_path); 193178479Sjb 194178479Sjb /* 195178479Sjb * Here we're marking socket as non-blocking and setting its backlog 196178479Sjb * to the maximum value 197178479Sjb */ 198178479Sjb chmod(config->socket_path, config->socket_mode); 199178479Sjb listen(retval->sockfd, -1); 200178479Sjb fcntl(retval->sockfd, F_SETFL, O_NONBLOCK); 201178479Sjb 202178479Sjb retval->queue = kqueue(); 203178479Sjb assert(retval->queue != -1); 204178479Sjb 205178479Sjb EV_SET(&eventlist, retval->sockfd, EVFILT_READ, EV_ADD | EV_ONESHOT, 206178479Sjb 0, 0, 0); 207178479Sjb memset(&timeout, 0, sizeof(struct timespec)); 208178479Sjb kevent(retval->queue, &eventlist, 1, NULL, 0, &timeout); 209178479Sjb 210178479Sjb LOG_MSG_2("runtime environment", "successfully initialized"); 211178479Sjb TRACE_OUT(init_runtime_env); 212178479Sjb return (retval); 213178479Sjb} 214178479Sjb 215178479Sjbstatic void 216178479Sjbdestroy_runtime_env(struct runtime_env *env) 217178479Sjb{ 218178479Sjb TRACE_IN(destroy_runtime_env); 219178479Sjb close(env->queue); 220178479Sjb close(env->sockfd); 221178479Sjb free(env); 222178479Sjb TRACE_OUT(destroy_runtime_env); 223178479Sjb} 224178479Sjb 225178479Sjbstatic void 226178479Sjbaccept_connection(struct kevent *event_data, struct runtime_env *env, 227178479Sjb struct configuration *config) 228178479Sjb{ 229178479Sjb struct kevent eventlist[2]; 230178479Sjb struct timespec timeout; 231178479Sjb struct query_state *qstate; 232178479Sjb 233178479Sjb int fd; 234178479Sjb int res; 235178479Sjb 236178479Sjb uid_t euid; 237178479Sjb gid_t egid; 238178479Sjb 239178479Sjb TRACE_IN(accept_connection); 240178479Sjb fd = accept(event_data->ident, NULL, NULL); 241178479Sjb if (fd == -1) { 242178479Sjb LOG_ERR_2("accept_connection", "error %d during accept()", 243178479Sjb errno); 244178479Sjb TRACE_OUT(accept_connection); 245178479Sjb return; 246178479Sjb } 247178479Sjb 248178479Sjb if (getpeereid(fd, &euid, &egid) != 0) { 249178479Sjb LOG_ERR_2("accept_connection", "error %d during getpeereid()", 250178479Sjb errno); 251178479Sjb TRACE_OUT(accept_connection); 252178479Sjb return; 253178479Sjb } 254178479Sjb 255178479Sjb qstate = init_query_state(fd, sizeof(int), euid, egid); 256178479Sjb if (qstate == NULL) { 257178479Sjb LOG_ERR_2("accept_connection", "can't init query_state"); 258178479Sjb TRACE_OUT(accept_connection); 259178479Sjb return; 260178479Sjb } 261178479Sjb 262178479Sjb memset(&timeout, 0, sizeof(struct timespec)); 263178479Sjb EV_SET(&eventlist[0], fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 264178479Sjb 0, qstate->timeout.tv_sec * 1000, qstate); 265178479Sjb EV_SET(&eventlist[1], fd, EVFILT_READ, EV_ADD | EV_ONESHOT, 266178479Sjb NOTE_LOWAT, qstate->kevent_watermark, qstate); 267178479Sjb res = kevent(env->queue, eventlist, 2, NULL, 0, &timeout); 268178479Sjb if (res < 0) 269178479Sjb LOG_ERR_2("accept_connection", "kevent error"); 270178479Sjb 271178479Sjb TRACE_OUT(accept_connection); 272178479Sjb} 273178479Sjb 274178479Sjbstatic void 275178479Sjbprocess_socket_event(struct kevent *event_data, struct runtime_env *env, 276178479Sjb struct configuration *config) 277178479Sjb{ 278178479Sjb struct kevent eventlist[2]; 279178479Sjb struct timeval query_timeout; 280178479Sjb struct timespec kevent_timeout; 281178479Sjb int nevents; 282178479Sjb int eof_res, res; 283178479Sjb ssize_t io_res; 284178479Sjb struct query_state *qstate; 285178479Sjb 286178479Sjb TRACE_IN(process_socket_event); 287178479Sjb eof_res = event_data->flags & EV_EOF ? 1 : 0; 288178479Sjb res = 0; 289178479Sjb 290178479Sjb memset(&kevent_timeout, 0, sizeof(struct timespec)); 291178479Sjb EV_SET(&eventlist[0], event_data->ident, EVFILT_TIMER, EV_DELETE, 292178479Sjb 0, 0, NULL); 293178479Sjb nevents = kevent(env->queue, eventlist, 1, NULL, 0, &kevent_timeout); 294178479Sjb if (nevents == -1) { 295178479Sjb if (errno == ENOENT) { 296178479Sjb /* the timer is already handling this event */ 297178479Sjb TRACE_OUT(process_socket_event); 298178479Sjb return; 299178479Sjb } else { 300178479Sjb /* some other error happened */ 301178479Sjb LOG_ERR_2("process_socket_event", "kevent error, errno" 302178479Sjb " is %d", errno); 303178479Sjb TRACE_OUT(process_socket_event); 304178479Sjb return; 305178479Sjb } 306178479Sjb } 307178479Sjb qstate = (struct query_state *)event_data->udata; 308178479Sjb 309178479Sjb /* 310178479Sjb * If the buffer that is to be send/received is too large, 311178479Sjb * we send it implicitly, by using query_io_buffer_read and 312178479Sjb * query_io_buffer_write functions in the query_state. These functions 313178479Sjb * use the temporary buffer, which is later send/received in parts. 314178479Sjb * The code below implements buffer splitting/mergind for send/receive 315178479Sjb * operations. It also does the actual socket IO operations. 316178479Sjb */ 317178479Sjb if (((qstate->use_alternate_io == 0) && 318178479Sjb (qstate->kevent_watermark <= event_data->data)) || 319178479Sjb ((qstate->use_alternate_io != 0) && 320178479Sjb (qstate->io_buffer_watermark <= event_data->data))) { 321178479Sjb if (qstate->use_alternate_io != 0) { 322178479Sjb switch (qstate->io_buffer_filter) { 323178479Sjb case EVFILT_READ: 324178479Sjb io_res = query_socket_read(qstate, 325178479Sjb qstate->io_buffer_p, 326178479Sjb qstate->io_buffer_watermark); 327178479Sjb if (io_res < 0) { 328178479Sjb qstate->use_alternate_io = 0; 329178479Sjb qstate->process_func = NULL; 330178479Sjb } else { 331178479Sjb qstate->io_buffer_p += io_res; 332178479Sjb if (qstate->io_buffer_p == 333178479Sjb qstate->io_buffer + 334178479Sjb qstate->io_buffer_size) { 335178479Sjb qstate->io_buffer_p = 336178479Sjb qstate->io_buffer; 337178479Sjb qstate->use_alternate_io = 0; 338178479Sjb } 339178479Sjb } 340178479Sjb break; 341178479Sjb default: 342178479Sjb break; 343178479Sjb } 344178479Sjb } 345178479Sjb 346178479Sjb if (qstate->use_alternate_io == 0) { 347178479Sjb do { 348178479Sjb res = qstate->process_func(qstate); 349178479Sjb } while ((qstate->kevent_watermark == 0) && 350178479Sjb (qstate->process_func != NULL) && 351178479Sjb (res == 0)); 352178479Sjb 353178479Sjb if (res != 0) 354178479Sjb qstate->process_func = NULL; 355178479Sjb } 356178479Sjb 357178479Sjb if ((qstate->use_alternate_io != 0) && 358178479Sjb (qstate->io_buffer_filter == EVFILT_WRITE)) { 359178479Sjb io_res = query_socket_write(qstate, qstate->io_buffer_p, 360178479Sjb qstate->io_buffer_watermark); 361178479Sjb if (io_res < 0) { 362178479Sjb qstate->use_alternate_io = 0; 363178479Sjb qstate->process_func = NULL; 364178479Sjb } else 365178479Sjb qstate->io_buffer_p += io_res; 366178479Sjb } 367178479Sjb } else { 368178479Sjb /* assuming that socket was closed */ 369178479Sjb qstate->process_func = NULL; 370178479Sjb qstate->use_alternate_io = 0; 371178479Sjb } 372178479Sjb 373178479Sjb if (((qstate->process_func == NULL) && 374178479Sjb (qstate->use_alternate_io == 0)) || 375178479Sjb (eof_res != 0) || (res != 0)) { 376178479Sjb destroy_query_state(qstate); 377178479Sjb close(event_data->ident); 378178479Sjb TRACE_OUT(process_socket_event); 379178479Sjb return; 380178479Sjb } 381178479Sjb 382178479Sjb /* updating the query_state lifetime variable */ 383178479Sjb get_time_func(&query_timeout); 384178479Sjb query_timeout.tv_usec = 0; 385178479Sjb query_timeout.tv_sec -= qstate->creation_time.tv_sec; 386178479Sjb if (query_timeout.tv_sec > qstate->timeout.tv_sec) 387178479Sjb query_timeout.tv_sec = 0; 388178479Sjb else 389178479Sjb query_timeout.tv_sec = qstate->timeout.tv_sec - 390178479Sjb query_timeout.tv_sec; 391178479Sjb 392178479Sjb if ((qstate->use_alternate_io != 0) && (qstate->io_buffer_p == 393178479Sjb qstate->io_buffer + qstate->io_buffer_size)) 394178479Sjb qstate->use_alternate_io = 0; 395178479Sjb 396178479Sjb if (qstate->use_alternate_io == 0) { 397178479Sjb /* 398178479Sjb * If we must send/receive the large block of data, 399178479Sjb * we should prepare the query_state's io_XXX fields. 400178479Sjb * We should also substitute its write_func and read_func 401178479Sjb * with the query_io_buffer_write and query_io_buffer_read, 402178479Sjb * which will allow us to implicitly send/receive this large 403178479Sjb * buffer later (in the subsequent calls to the 404178479Sjb * process_socket_event). 405178479Sjb */ 406178479Sjb if (qstate->kevent_watermark > MAX_SOCKET_IO_SIZE) { 407178479Sjb if (qstate->io_buffer != NULL) 408178479Sjb free(qstate->io_buffer); 409178479Sjb 410178479Sjb qstate->io_buffer = (char *)calloc(1, 411178479Sjb qstate->kevent_watermark); 412178479Sjb assert(qstate->io_buffer != NULL); 413178479Sjb 414178479Sjb qstate->io_buffer_p = qstate->io_buffer; 415178479Sjb qstate->io_buffer_size = qstate->kevent_watermark; 416178479Sjb qstate->io_buffer_filter = qstate->kevent_filter; 417178479Sjb 418178479Sjb qstate->write_func = query_io_buffer_write; 419178479Sjb qstate->read_func = query_io_buffer_read; 420178479Sjb 421178479Sjb if (qstate->kevent_filter == EVFILT_READ) 422178479Sjb qstate->use_alternate_io = 1; 423178479Sjb 424178479Sjb qstate->io_buffer_watermark = MAX_SOCKET_IO_SIZE; 425178479Sjb EV_SET(&eventlist[1], event_data->ident, 426178479Sjb qstate->kevent_filter, EV_ADD | EV_ONESHOT, 427178479Sjb NOTE_LOWAT, MAX_SOCKET_IO_SIZE, qstate); 428178479Sjb } else { 429178479Sjb EV_SET(&eventlist[1], event_data->ident, 430178479Sjb qstate->kevent_filter, EV_ADD | EV_ONESHOT, 431178479Sjb NOTE_LOWAT, qstate->kevent_watermark, qstate); 432178479Sjb } 433178479Sjb } else { 434178479Sjb if (qstate->io_buffer + qstate->io_buffer_size - 435178479Sjb qstate->io_buffer_p < 436178479Sjb MAX_SOCKET_IO_SIZE) { 437178479Sjb qstate->io_buffer_watermark = qstate->io_buffer + 438178479Sjb qstate->io_buffer_size - qstate->io_buffer_p; 439178479Sjb EV_SET(&eventlist[1], event_data->ident, 440178479Sjb qstate->io_buffer_filter, 441178479Sjb EV_ADD | EV_ONESHOT, NOTE_LOWAT, 442178479Sjb qstate->io_buffer_watermark, 443178479Sjb qstate); 444178479Sjb } else { 445178479Sjb qstate->io_buffer_watermark = MAX_SOCKET_IO_SIZE; 446178479Sjb EV_SET(&eventlist[1], event_data->ident, 447178479Sjb qstate->io_buffer_filter, EV_ADD | EV_ONESHOT, 448178479Sjb NOTE_LOWAT, MAX_SOCKET_IO_SIZE, qstate); 449178479Sjb } 450178479Sjb } 451178479Sjb EV_SET(&eventlist[0], event_data->ident, EVFILT_TIMER, 452178479Sjb EV_ADD | EV_ONESHOT, 0, query_timeout.tv_sec * 1000, qstate); 453178479Sjb kevent(env->queue, eventlist, 2, NULL, 0, &kevent_timeout); 454178479Sjb 455178479Sjb TRACE_OUT(process_socket_event); 456178479Sjb} 457178479Sjb 458178479Sjb/* 459178479Sjb * This routine is called if timer event has been signaled in the kqueue. It 460178479Sjb * just closes the socket and destroys the query_state. 461178479Sjb */ 462178479Sjbstatic void 463178479Sjbprocess_timer_event(struct kevent *event_data, struct runtime_env *env, 464178479Sjb struct configuration *config) 465178479Sjb{ 466178479Sjb struct query_state *qstate; 467178479Sjb 468178479Sjb TRACE_IN(process_timer_event); 469178479Sjb qstate = (struct query_state *)event_data->udata; 470268578Srpaulo destroy_query_state(qstate); 471268578Srpaulo close(event_data->ident); 472268578Srpaulo TRACE_OUT(process_timer_event); 473178479Sjb} 474178479Sjb 475178479Sjb/* 476178479Sjb * Processing loop is the basic processing routine, that forms a body of each 477178479Sjb * procssing thread 478178479Sjb */ 479178479Sjbstatic void 480178479Sjbprocessing_loop(cache the_cache, struct runtime_env *env, 481178479Sjb struct configuration *config) 482178479Sjb{ 483178479Sjb struct timespec timeout; 484178479Sjb const int eventlist_size = 1; 485178479Sjb struct kevent eventlist[eventlist_size]; 486178479Sjb int nevents, i; 487178479Sjb 488178479Sjb TRACE_MSG("=> processing_loop"); 489178479Sjb memset(&timeout, 0, sizeof(struct timespec)); 490178479Sjb memset(&eventlist, 0, sizeof(struct kevent) * eventlist_size); 491178479Sjb 492178479Sjb for (;;) { 493178479Sjb nevents = kevent(env->queue, NULL, 0, eventlist, 494178479Sjb eventlist_size, NULL); 495178479Sjb /* 496268578Srpaulo * we can only receive 1 event on success 497268578Srpaulo */ 498268578Srpaulo if (nevents == 1) { 499268578Srpaulo struct kevent *event_data; 500268578Srpaulo event_data = &eventlist[0]; 501268578Srpaulo 502268578Srpaulo if (event_data->ident == env->sockfd) { 503268578Srpaulo for (i = 0; i < event_data->data; ++i) 504268578Srpaulo accept_connection(event_data, env, config); 505268578Srpaulo 506268578Srpaulo EV_SET(eventlist, s_runtime_env->sockfd, 507268578Srpaulo EVFILT_READ, EV_ADD | EV_ONESHOT, 508268578Srpaulo 0, 0, 0); 509268578Srpaulo memset(&timeout, 0, 510268578Srpaulo sizeof(struct timespec)); 511268578Srpaulo kevent(s_runtime_env->queue, eventlist, 512268578Srpaulo 1, NULL, 0, &timeout); 513268578Srpaulo 514268578Srpaulo } else { 515268578Srpaulo switch (event_data->filter) { 516268578Srpaulo case EVFILT_READ: 517268578Srpaulo case EVFILT_WRITE: 518268578Srpaulo process_socket_event(event_data, 519268578Srpaulo env, config); 520268578Srpaulo break; 521268578Srpaulo case EVFILT_TIMER: 522178479Sjb process_timer_event(event_data, 523178479Sjb env, config); 524178479Sjb break; 525178479Sjb default: 526178479Sjb break; 527178479Sjb } 528178479Sjb } 529178479Sjb } else { 530178479Sjb /* this branch shouldn't be currently executed */ 531178479Sjb } 532178479Sjb } 533178479Sjb 534178479Sjb TRACE_MSG("<= processing_loop"); 535178479Sjb} 536178479Sjb 537178479Sjb/* 538178479Sjb * Wrapper above the processing loop function. It sets the thread signal mask 539178479Sjb * to avoid SIGPIPE signals (which can happen if the client works incorrectly). 540178479Sjb */ 541178479Sjbstatic void * 542178479Sjbprocessing_thread(void *data) 543178479Sjb{ 544178479Sjb struct processing_thread_args *args; 545178479Sjb sigset_t new; 546178479Sjb 547178479Sjb TRACE_MSG("=> processing_thread"); 548178479Sjb args = (struct processing_thread_args *)data; 549178479Sjb 550178479Sjb sigemptyset(&new); 551178479Sjb sigaddset(&new, SIGPIPE); 552178479Sjb if (pthread_sigmask(SIG_BLOCK, &new, NULL) != 0) 553178479Sjb LOG_ERR_1("processing thread", 554178479Sjb "thread can't block the SIGPIPE signal"); 555210767Srpaulo 556178479Sjb processing_loop(args->the_cache, args->the_runtime_env, 557178479Sjb args->the_configuration); 558178479Sjb free(args); 559178479Sjb TRACE_MSG("<= processing_thread"); 560178479Sjb 561178479Sjb return (NULL); 562178479Sjb} 563178479Sjb 564178479Sjbvoid 565178479Sjbget_time_func(struct timeval *time) 566178479Sjb{ 567178479Sjb struct timespec res; 568178479Sjb memset(&res, 0, sizeof(struct timespec)); 569178479Sjb clock_gettime(CLOCK_MONOTONIC, &res); 570178479Sjb 571178479Sjb time->tv_sec = res.tv_sec; 572178479Sjb time->tv_usec = 0; 573178479Sjb} 574178479Sjb 575178479Sjb/* 576297077Smav * The idea of _nss_cache_cycle_prevention_function is that nsdispatch will 577178479Sjb * search for this symbol in the executable. This symbol is the attribute of 578178559Sjb * the caching daemon. So, if it exists, nsdispatch won't try to connect to 579178559Sjb * the caching daemon and will just ignore the 'cache' source in the 580178559Sjb * nsswitch.conf. This method helps to avoid cycles and organize 581178559Sjb * self-performing requests. 582178559Sjb */ 583178479Sjbvoid 584178479Sjb_nss_cache_cycle_prevention_function(void) 585178479Sjb{ 586178479Sjb} 587178479Sjb 588178479Sjbint 589178479Sjbmain(int argc, char *argv[]) 590178479Sjb{ 591268578Srpaulo struct processing_thread_args *thread_args; 592268578Srpaulo pthread_t *threads; 593268578Srpaulo 594268578Srpaulo struct pidfh *pidfile; 595268578Srpaulo pid_t pid; 596268578Srpaulo 597268578Srpaulo char const *config_file; 598268578Srpaulo char const *error_str; 599268578Srpaulo int error_line; 600268578Srpaulo int i, res; 601268578Srpaulo 602268578Srpaulo int trace_mode_enabled; 603268578Srpaulo int force_single_threaded; 604268578Srpaulo int do_not_daemonize; 605268578Srpaulo int clear_user_cache_entries, clear_all_cache_entries; 606268578Srpaulo char *user_config_entry_name, *global_config_entry_name; 607268578Srpaulo int show_statistics; 608268578Srpaulo int daemon_mode, interactive_mode; 609268578Srpaulo 610268578Srpaulo 611268578Srpaulo /* by default all debug messages are omitted */ 612268578Srpaulo TRACE_OFF(); 613268578Srpaulo 614268578Srpaulo /* parsing command line arguments */ 615268578Srpaulo trace_mode_enabled = 0; 616268578Srpaulo force_single_threaded = 0; 617268578Srpaulo do_not_daemonize = 0; 618268578Srpaulo clear_user_cache_entries = 0; 619268578Srpaulo clear_all_cache_entries = 0; 620268578Srpaulo show_statistics = 0; 621268578Srpaulo user_config_entry_name = NULL; 622268578Srpaulo global_config_entry_name = NULL; 623268578Srpaulo while ((res = getopt(argc, argv, "nstdi:I:")) != -1) { 624268578Srpaulo switch (res) { 625268578Srpaulo case 'n': 626268578Srpaulo do_not_daemonize = 1; 627268578Srpaulo break; 628268578Srpaulo case 's': 629268578Srpaulo force_single_threaded = 1; 630268578Srpaulo break; 631268578Srpaulo case 't': 632268578Srpaulo trace_mode_enabled = 1; 633268578Srpaulo break; 634268578Srpaulo case 'i': 635268578Srpaulo clear_user_cache_entries = 1; 636268578Srpaulo if (optarg != NULL) 637268578Srpaulo if (strcmp(optarg, "all") != 0) 638268578Srpaulo user_config_entry_name = strdup(optarg); 639268578Srpaulo break; 640268578Srpaulo case 'I': 641268578Srpaulo clear_all_cache_entries = 1; 642268578Srpaulo if (optarg != NULL) 643268578Srpaulo if (strcmp(optarg, "all") != 0) 644268578Srpaulo global_config_entry_name = 645268578Srpaulo strdup(optarg); 646268578Srpaulo break; 647268578Srpaulo case 'd': 648268578Srpaulo show_statistics = 1; 649268578Srpaulo break; 650268578Srpaulo case '?': 651268578Srpaulo default: 652268578Srpaulo usage(); 653268578Srpaulo /* NOT REACHED */ 654268578Srpaulo } 655268578Srpaulo } 656268578Srpaulo 657268578Srpaulo daemon_mode = do_not_daemonize | force_single_threaded | 658268578Srpaulo trace_mode_enabled; 659268578Srpaulo interactive_mode = clear_user_cache_entries | clear_all_cache_entries | 660268578Srpaulo show_statistics; 661268578Srpaulo 662268578Srpaulo if ((daemon_mode != 0) && (interactive_mode != 0)) { 663268578Srpaulo LOG_ERR_1("main", "daemon mode and interactive_mode arguments " 664268578Srpaulo "can't be used together"); 665268578Srpaulo usage(); 666268578Srpaulo } 667268578Srpaulo 668268578Srpaulo if (interactive_mode != 0) { 669268578Srpaulo FILE *pidfin = fopen(DEFAULT_PIDFILE_PATH, "r"); 670268578Srpaulo char pidbuf[256]; 671268578Srpaulo 672268578Srpaulo struct nscd_connection_params connection_params; 673268578Srpaulo nscd_connection connection; 674268578Srpaulo 675268578Srpaulo int result; 676268578Srpaulo 677268578Srpaulo if (pidfin == NULL) 678268578Srpaulo errx(EXIT_FAILURE, "There is no daemon running."); 679268578Srpaulo 680268578Srpaulo memset(pidbuf, 0, sizeof(pidbuf)); 681268578Srpaulo fread(pidbuf, sizeof(pidbuf) - 1, 1, pidfin); 682268578Srpaulo fclose(pidfin); 683268578Srpaulo 684268578Srpaulo if (ferror(pidfin) != 0) 685268578Srpaulo errx(EXIT_FAILURE, "Can't read from pidfile."); 686268578Srpaulo 687268578Srpaulo if (sscanf(pidbuf, "%d", &pid) != 1) 688268578Srpaulo errx(EXIT_FAILURE, "Invalid pidfile."); 689268578Srpaulo LOG_MSG_1("main", "daemon PID is %d", pid); 690268578Srpaulo 691268578Srpaulo 692268578Srpaulo memset(&connection_params, 0, 693268578Srpaulo sizeof(struct nscd_connection_params)); 694268578Srpaulo connection_params.socket_path = DEFAULT_SOCKET_PATH; 695268578Srpaulo connection = open_nscd_connection__(&connection_params); 696268578Srpaulo if (connection == INVALID_NSCD_CONNECTION) 697297953Smarkj errx(EXIT_FAILURE, "Can't connect to the daemon."); 698268578Srpaulo 699268578Srpaulo if (clear_user_cache_entries != 0) { 700268578Srpaulo result = nscd_transform__(connection, 701268578Srpaulo user_config_entry_name, TT_USER); 702268578Srpaulo if (result != 0) 703268578Srpaulo LOG_MSG_1("main", 704268578Srpaulo "user cache transformation failed"); 705268578Srpaulo else 706268578Srpaulo LOG_MSG_1("main", 707268578Srpaulo "user cache_transformation " 708268578Srpaulo "succeeded"); 709297129Spfg } 710268578Srpaulo 711268578Srpaulo if (clear_all_cache_entries != 0) { 712268578Srpaulo if (geteuid() != 0) 713268578Srpaulo errx(EXIT_FAILURE, "Only root can initiate " 714268578Srpaulo "global cache transformation."); 715268578Srpaulo 716297129Spfg result = nscd_transform__(connection, 717268578Srpaulo global_config_entry_name, TT_ALL); 718268578Srpaulo if (result != 0) 719268578Srpaulo LOG_MSG_1("main", 720268578Srpaulo "global cache transformation " 721268578Srpaulo "failed"); 722268578Srpaulo else 723268578Srpaulo LOG_MSG_1("main", 724268578Srpaulo "global cache transformation " 725268578Srpaulo "succeeded"); 726268578Srpaulo } 727268578Srpaulo 728268578Srpaulo close_nscd_connection__(connection); 729268578Srpaulo 730268578Srpaulo free(user_config_entry_name); 731268578Srpaulo free(global_config_entry_name); 732268578Srpaulo return (EXIT_SUCCESS); 733268578Srpaulo } 734268578Srpaulo 735268578Srpaulo pidfile = pidfile_open(DEFAULT_PIDFILE_PATH, 0644, &pid); 736268578Srpaulo if (pidfile == NULL) { 737268578Srpaulo if (errno == EEXIST) 738268578Srpaulo errx(EXIT_FAILURE, "Daemon already running, pid: %d.", 739268578Srpaulo pid); 740268578Srpaulo warn("Cannot open or create pidfile"); 741268578Srpaulo } 742268578Srpaulo 743268578Srpaulo if (trace_mode_enabled == 1) 744178479Sjb TRACE_ON(); 745178479Sjb 746178479Sjb /* blocking the main thread from receiving SIGPIPE signal */ 747178479Sjb sigblock(sigmask(SIGPIPE)); 748178479Sjb 749178479Sjb /* daemonization */ 750268578Srpaulo if (do_not_daemonize == 0) { 751268578Srpaulo res = daemon(0, trace_mode_enabled == 0 ? 0 : 1); 752268578Srpaulo if (res != 0) { 753178479Sjb LOG_ERR_1("main", "can't daemonize myself: %s", 754178479Sjb strerror(errno)); 755178479Sjb pidfile_remove(pidfile); 756178479Sjb goto fin; 757178479Sjb } else 758178479Sjb LOG_MSG_1("main", "successfully daemonized"); 759178479Sjb } 760178479Sjb 761178479Sjb pidfile_write(pidfile); 762178479Sjb 763178479Sjb s_agent_table = init_agent_table(); 764178479Sjb register_agent(s_agent_table, init_passwd_agent()); 765178479Sjb register_agent(s_agent_table, init_passwd_mp_agent()); 766178479Sjb register_agent(s_agent_table, init_group_agent()); 767178479Sjb register_agent(s_agent_table, init_group_mp_agent()); 768178479Sjb register_agent(s_agent_table, init_services_agent()); 769178479Sjb register_agent(s_agent_table, init_services_mp_agent()); 770178479Sjb LOG_MSG_1("main", "request agents registered successfully"); 771178479Sjb 772178479Sjb /* 773178479Sjb * Hosts agent can't work properly until we have access to the 774178479Sjb * appropriate dtab structures, which are used in nsdispatch 775178479Sjb * calls 776178479Sjb * 777178479Sjb register_agent(s_agent_table, init_hosts_agent()); 778178479Sjb */ 779178479Sjb 780178479Sjb /* configuration initialization */ 781178479Sjb s_configuration = init_configuration(); 782178479Sjb fill_configuration_defaults(s_configuration); 783178479Sjb 784178479Sjb error_str = NULL; 785178479Sjb error_line = 0; 786178479Sjb config_file = CONFIG_PATH; 787178479Sjb 788178479Sjb res = parse_config_file(s_configuration, config_file, &error_str, 789178479Sjb &error_line); 790178479Sjb if ((res != 0) && (error_str == NULL)) { 791178479Sjb config_file = DEFAULT_CONFIG_PATH; 792178479Sjb res = parse_config_file(s_configuration, config_file, 793178479Sjb &error_str, &error_line); 794178479Sjb } 795178479Sjb 796178479Sjb if (res != 0) { 797178479Sjb if (error_str != NULL) { 798178479Sjb LOG_ERR_1("main", "error in configuration file(%s, %d): %s\n", 799178479Sjb config_file, error_line, error_str); 800178479Sjb } else { 801178479Sjb LOG_ERR_1("main", "no configuration file found " 802178479Sjb "- was looking for %s and %s", 803297129Spfg CONFIG_PATH, DEFAULT_CONFIG_PATH); 804297129Spfg } 805178479Sjb destroy_configuration(s_configuration); 806178479Sjb return (-1); 807178479Sjb } 808178479Sjb 809178479Sjb if (force_single_threaded == 1) 810178479Sjb s_configuration->threads_num = 1; 811178479Sjb 812178479Sjb /* cache initialization */ 813178479Sjb s_cache = init_cache_(s_configuration); 814178479Sjb if (s_cache == NULL) { 815178479Sjb LOG_ERR_1("main", "can't initialize the cache"); 816178479Sjb destroy_configuration(s_configuration); 817178479Sjb return (-1); 818178479Sjb } 819178479Sjb 820178479Sjb /* runtime environment initialization */ 821178479Sjb s_runtime_env = init_runtime_env(s_configuration); 822178479Sjb if (s_runtime_env == NULL) { 823178479Sjb LOG_ERR_1("main", "can't initialize the runtime environment"); 824178479Sjb destroy_configuration(s_configuration); 825178479Sjb destroy_cache_(s_cache); 826178479Sjb return (-1); 827178479Sjb } 828178479Sjb 829178479Sjb if (s_configuration->threads_num > 1) { 830178479Sjb threads = (pthread_t *)calloc(1, sizeof(pthread_t) * 831178479Sjb s_configuration->threads_num); 832178479Sjb for (i = 0; i < s_configuration->threads_num; ++i) { 833178479Sjb thread_args = (struct processing_thread_args *)malloc( 834178479Sjb sizeof(struct processing_thread_args)); 835268578Srpaulo thread_args->the_cache = s_cache; 836268578Srpaulo thread_args->the_runtime_env = s_runtime_env; 837268578Srpaulo thread_args->the_configuration = s_configuration; 838268578Srpaulo 839268578Srpaulo LOG_MSG_1("main", "thread #%d was successfully created", 840268578Srpaulo i); 841268578Srpaulo pthread_create(&threads[i], NULL, processing_thread, 842268578Srpaulo thread_args); 843178479Sjb 844178479Sjb thread_args = NULL; 845178479Sjb } 846178479Sjb 847178479Sjb for (i = 0; i < s_configuration->threads_num; ++i) 848178479Sjb pthread_join(threads[i], NULL); 849178479Sjb } else { 850178479Sjb LOG_MSG_1("main", "working in single-threaded mode"); 851178479Sjb processing_loop(s_cache, s_runtime_env, s_configuration); 852178479Sjb } 853178479Sjb 854178479Sjbfin: 855178479Sjb /* runtime environment destruction */ 856178479Sjb destroy_runtime_env(s_runtime_env); 857178479Sjb 858178479Sjb /* cache destruction */ 859178479Sjb destroy_cache_(s_cache); 860178479Sjb 861178479Sjb /* configuration destruction */ 862178479Sjb destroy_configuration(s_configuration); 863178479Sjb 864178479Sjb /* agents table destruction */ 865178479Sjb destroy_agent_table(s_agent_table); 866178479Sjb 867178479Sjb pidfile_remove(pidfile); 868178479Sjb return (EXIT_SUCCESS); 869178479Sjb} 870178479Sjb