extractor_ipc_gnu.c (16140B)
1 /* 2 This file is part of libextractor. 3 Copyright (C) 2012 Vidyut Samanta and Christian Grothoff 4 5 libextractor is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published 7 by the Free Software Foundation; either version 3, or (at your 8 option) any later version. 9 10 libextractor is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with libextractor; see the file COPYING. If not, write to the 17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 Boston, MA 02110-1301, USA. 19 */ 20 /** 21 * @file main/extractor_ipc_gnu.c 22 * @brief IPC with plugin for GNU/POSIX systems 23 * @author Christian Grothoff 24 */ 25 #include "platform.h" 26 #include "extractor.h" 27 #include "extractor_datasource.h" 28 #include "extractor_logging.h" 29 #include "extractor_plugin_main.h" 30 #include "extractor_plugins.h" 31 #include "extractor_ipc.h" 32 #include <dirent.h> 33 #include <sys/types.h> 34 #include <sys/wait.h> 35 #include <sys/shm.h> 36 #include <signal.h> 37 #if HAVE_SYS_APPARMOR_H 38 #include <sys/apparmor.h> 39 #endif 40 41 /** 42 * A shared memory resource (often shared with several 43 * other processes). 44 */ 45 struct EXTRACTOR_SharedMemory 46 { 47 /** 48 * Pointer to the mapped region of the shm (covers the whole shm) 49 */ 50 void *shm_ptr; 51 52 /** 53 * Allocated size of the shm 54 */ 55 size_t shm_size; 56 57 /** 58 * POSIX id of the shm into which data is uncompressed 59 */ 60 int shm_id; 61 62 /** 63 * Name of the shm 64 */ 65 char shm_name[MAX_SHM_NAME + 1]; 66 67 /** 68 * Reference counter describing how many references share this SHM. 69 */ 70 unsigned int rc; 71 72 }; 73 74 75 /** 76 * Definition of an IPC communication channel with 77 * some plugin. 78 */ 79 struct EXTRACTOR_Channel 80 { 81 82 /** 83 * Buffer for reading data from the plugin. 84 */ 85 char *mdata; 86 87 /** 88 * Size of the @e mdata buffer. 89 */ 90 size_t mdata_size; 91 92 /** 93 * Memory segment shared with this process. 94 */ 95 struct EXTRACTOR_SharedMemory *shm; 96 97 /** 98 * The plugin this channel is to communicate with. 99 */ 100 struct EXTRACTOR_PluginList *plugin; 101 102 /** 103 * Pipe used to communicate information to the plugin child process. 104 * NULL if not initialized. 105 */ 106 int cpipe_in; 107 108 /** 109 * Number of valid bytes in the channel's buffer. 110 */ 111 size_t size; 112 113 /** 114 * Pipe used to read information about extracted meta data from 115 * the plugin child process. -1 if not initialized. 116 */ 117 int cpipe_out; 118 119 /** 120 * Process ID of the child process for this plugin. 0 for none. 121 */ 122 pid_t cpid; 123 124 }; 125 126 127 /** 128 * Create a shared memory area. 129 * 130 * @param size size of the shared area 131 * @return NULL on error 132 */ 133 struct EXTRACTOR_SharedMemory * 134 EXTRACTOR_IPC_shared_memory_create_ (size_t size) 135 { 136 struct EXTRACTOR_SharedMemory *shm; 137 138 if (NULL == (shm = malloc (sizeof (struct EXTRACTOR_SharedMemory)))) 139 { 140 LOG_STRERROR ("malloc"); 141 return NULL; 142 } 143 /* POSIX only guarantees portability for a shared memory object name 144 that consists of an initial slash followed by one or more 145 characters, none of which is a slash. The name is not a file 146 system path: NetBSD keeps POSIX shared memory in a tmpfs of its 147 own on /var/shm and rejects any name with an embedded slash with 148 EINVAL, so prefixing a directory such as /tmp made every call to 149 shm_open() fail there. */ 150 snprintf (shm->shm_name, 151 MAX_SHM_NAME, 152 "/LE-%u-%u", 153 getpid (), 154 (unsigned int) random ()); 155 if (-1 == (shm->shm_id = shm_open (shm->shm_name, 156 O_RDWR | O_CREAT, 157 S_IRUSR | S_IWUSR))) 158 { 159 LOG_STRERROR_FILE ("shm_open", 160 shm->shm_name); 161 free (shm); 162 return NULL; 163 } 164 if ( (0 != ftruncate (shm->shm_id, size)) || 165 (NULL == (shm->shm_ptr = mmap (NULL, 166 size, 167 PROT_WRITE, 168 MAP_SHARED, 169 shm->shm_id, 170 0))) || 171 (((void*) -1) == shm->shm_ptr) ) 172 { 173 LOG_STRERROR ("ftruncate/mmap"); 174 (void) close (shm->shm_id); 175 (void) shm_unlink (shm->shm_name); 176 free (shm); 177 return NULL; 178 } 179 shm->shm_size = size; 180 shm->rc = 0; 181 return shm; 182 } 183 184 185 /** 186 * Change the reference counter for this shm instance. 187 * 188 * @param shm instance to update 189 * @param delta value to change RC by 190 * @return new RC 191 */ 192 unsigned int 193 EXTRACTOR_IPC_shared_memory_change_rc_ (struct EXTRACTOR_SharedMemory *shm, 194 int delta) 195 { 196 shm->rc += delta; 197 return shm->rc; 198 } 199 200 201 /** 202 * Destroy shared memory area. 203 * 204 * @param shm memory area to destroy 205 * @return NULL on error 206 */ 207 void 208 EXTRACTOR_IPC_shared_memory_destroy_ (struct EXTRACTOR_SharedMemory *shm) 209 { 210 munmap (shm->shm_ptr, 211 shm->shm_size); 212 (void) close (shm->shm_id); 213 (void) shm_unlink (shm->shm_name); 214 free (shm); 215 } 216 217 218 /** 219 * Initialize shared memory area from data source. 220 * 221 * @param shm memory area to initialize 222 * @param ds data source to use for initialization 223 * @param off offset to use in data source 224 * @param size number of bytes to copy 225 * @return -1 on error, otherwise number of bytes copied 226 */ 227 ssize_t 228 EXTRACTOR_IPC_shared_memory_set_ (struct EXTRACTOR_SharedMemory *shm, 229 struct EXTRACTOR_Datasource *ds, 230 uint64_t off, 231 size_t size) 232 { 233 if (-1 == 234 EXTRACTOR_datasource_seek_ (ds, 235 off, 236 SEEK_SET)) 237 { 238 LOG ("Failed to set IPC memory due to seek error\n"); 239 return -1; 240 } 241 if (size > shm->shm_size) 242 size = shm->shm_size; 243 return EXTRACTOR_datasource_read_ (ds, 244 shm->shm_ptr, 245 size); 246 } 247 248 249 /** 250 * Query datasource for current position 251 * 252 * @param ds data source to query 253 * @return current position in the datasource or UINT64_MAX on error 254 */ 255 uint64_t 256 EXTRACTOR_datasource_get_pos_ (struct EXTRACTOR_Datasource *ds) 257 { 258 int64_t pos = EXTRACTOR_datasource_seek_ (ds, 259 0, 260 SEEK_CUR); 261 262 if (-1 == pos) 263 return UINT64_MAX; 264 return pos; 265 } 266 267 268 /** 269 * Create a channel to communicate with a process wrapping 270 * the plugin of the given name. Starts the process as well. 271 * 272 * @param plugin the plugin 273 * @param shm memory to share with the process 274 * @return NULL on error, otherwise IPC channel 275 */ 276 struct EXTRACTOR_Channel * 277 EXTRACTOR_IPC_channel_create_ (struct EXTRACTOR_PluginList *plugin, 278 struct EXTRACTOR_SharedMemory *shm) 279 { 280 struct EXTRACTOR_Channel *channel; 281 int p1[2]; 282 int p2[2]; 283 pid_t pid; 284 struct InitMessage *init; 285 size_t slen; 286 static int sigpipe_ignored; 287 288 if (! sigpipe_ignored) 289 { 290 /* The cpipe_in end may be written to after the plugin child 291 has died; without ignoring SIGPIPE this would terminate the 292 host application. As the channel uses pipes (not sockets), 293 MSG_NOSIGNAL is not applicable, so we ignore SIGPIPE here. */ 294 signal (SIGPIPE, SIG_IGN); 295 sigpipe_ignored = 1; 296 } 297 if (NULL == (channel = malloc (sizeof (struct EXTRACTOR_Channel)))) 298 { 299 LOG_STRERROR ("malloc"); 300 return NULL; 301 } 302 channel->mdata_size = 1024; 303 if (NULL == (channel->mdata = malloc (channel->mdata_size))) 304 { 305 LOG_STRERROR ("malloc"); 306 free (channel); 307 return NULL; 308 } 309 channel->shm = shm; 310 channel->plugin = plugin; 311 channel->size = 0; 312 if (0 != pipe (p1)) 313 { 314 LOG_STRERROR ("pipe"); 315 free (channel->mdata); 316 free (channel); 317 return NULL; 318 } 319 if (0 != pipe (p2)) 320 { 321 LOG_STRERROR ("pipe"); 322 (void) close (p1[0]); 323 (void) close (p1[1]); 324 free (channel->mdata); 325 free (channel); 326 return NULL; 327 } 328 pid = fork (); 329 if (pid == -1) 330 { 331 LOG_STRERROR ("fork"); 332 (void) close (p1[0]); 333 (void) close (p1[1]); 334 (void) close (p2[0]); 335 (void) close (p2[1]); 336 free (channel->mdata); 337 free (channel); 338 return NULL; 339 } 340 if (0 == pid) 341 { 342 (void) close (p1[1]); 343 (void) close (p2[0]); 344 free (channel->mdata); 345 free (channel); 346 #if HAVE_SYS_APPARMOR_H 347 #if HAVE_APPARMOR 348 if (0 > aa_change_profile ("libextractor")) 349 { 350 int eno = errno; 351 352 if ( (EINVAL != eno) && 353 (ENOENT != eno) ) 354 { 355 fprintf (stderr, 356 "Failure changing AppArmor profile: %s\n", 357 strerror (errno)); 358 _exit (1); 359 } 360 } 361 #endif 362 #endif 363 EXTRACTOR_plugin_main_ (plugin, p1[0], p2[1]); 364 _exit (0); 365 } 366 (void) close (p1[0]); 367 (void) close (p2[1]); 368 channel->cpipe_in = p1[1]; 369 channel->cpipe_out = p2[0]; 370 channel->cpid = pid; 371 slen = strlen (shm->shm_name) + 1; 372 if (NULL == (init = malloc (sizeof (struct InitMessage) + slen))) 373 { 374 LOG_STRERROR ("malloc"); 375 EXTRACTOR_IPC_channel_destroy_ (channel); 376 return NULL; 377 } 378 init->opcode = MESSAGE_INIT_STATE; 379 init->reserved = 0; 380 init->reserved2 = 0; 381 init->shm_name_length = slen; 382 init->shm_map_size = shm->shm_size; 383 memcpy (&init[1], shm->shm_name, slen); 384 if (sizeof (struct InitMessage) + slen != 385 EXTRACTOR_IPC_channel_send_ (channel, 386 init, 387 sizeof (struct InitMessage) + slen) ) 388 { 389 LOG ("Failed to send INIT_STATE message to plugin\n"); 390 EXTRACTOR_IPC_channel_destroy_ (channel); 391 free (init); 392 return NULL; 393 } 394 free (init); 395 return channel; 396 } 397 398 399 /** 400 * Destroy communication channel with a plugin/process. Also 401 * destroys the process. 402 * 403 * @param channel channel to communicate with the plugin 404 */ 405 void 406 EXTRACTOR_IPC_channel_destroy_ (struct EXTRACTOR_Channel *channel) 407 { 408 int status; 409 410 if (0 != kill (channel->cpid, SIGKILL)) 411 LOG_STRERROR ("kill"); 412 if (-1 == waitpid (channel->cpid, &status, 0)) 413 LOG_STRERROR ("waitpid"); 414 if (0 != close (channel->cpipe_out)) 415 LOG_STRERROR ("close"); 416 if (0 != close (channel->cpipe_in)) 417 LOG_STRERROR ("close"); 418 if (NULL != channel->plugin) 419 channel->plugin->channel = NULL; 420 free (channel->mdata); 421 free (channel); 422 } 423 424 425 /** 426 * Send data via the given IPC channel (blocking). 427 * 428 * @param channel channel to communicate with the plugin 429 * @param buf data to send 430 * @param size number of bytes in buf to send 431 * @return -1 on error, number of bytes sent on success 432 * (never does partial writes) 433 */ 434 ssize_t 435 EXTRACTOR_IPC_channel_send_ (struct EXTRACTOR_Channel *channel, 436 const void *data, 437 size_t size) 438 { 439 const char *cdata = data; 440 size_t off = 0; 441 ssize_t ret; 442 443 while (off < size) 444 { 445 ret = write (channel->cpipe_in, &cdata[off], size - off); 446 if (ret <= 0) 447 { 448 if ((-1 == ret) && (EINTR == errno)) 449 continue; 450 if (-1 == ret) 451 LOG_STRERROR ("write"); 452 return -1; 453 } 454 off += ret; 455 } 456 return size; 457 } 458 459 460 /** 461 * Receive data from any of the given IPC channels (blocking). 462 * Wait for one of the plugins to reply. 463 * Selects on plugin output pipes, runs 'receive_reply' 464 * on each activated pipe until it gets a seek request 465 * or a done message. Called repeatedly by the user until all pipes are dry or 466 * broken. 467 * 468 * @param channels array of channels, channels that break may be set to NULL 469 * @param num_channels length of the @a channels array 470 * @param proc function to call to process messages (may be called 471 * more than once) 472 * @param proc_cls closure for @a proc 473 * @return -1 on error, 1 on success 474 */ 475 int 476 EXTRACTOR_IPC_channel_recv_ (struct EXTRACTOR_Channel **channels, 477 unsigned int num_channels, 478 EXTRACTOR_ChannelMessageProcessor proc, 479 void *proc_cls) 480 { 481 struct timeval tv; 482 fd_set to_check; 483 int max; 484 unsigned int i; 485 struct EXTRACTOR_Channel *channel; 486 ssize_t ret; 487 ssize_t iret; 488 char *ndata; 489 int closed_channel; 490 491 FD_ZERO (&to_check); 492 max = -1; 493 for (i = 0; i<num_channels; i++) 494 { 495 channel = channels[i]; 496 if (NULL == channel) 497 continue; 498 FD_SET (channel->cpipe_out, &to_check); 499 if (max < channel->cpipe_out) 500 max = channel->cpipe_out; 501 } 502 if (-1 == max) 503 { 504 return 1; /* nothing left to do! */ 505 } 506 tv.tv_sec = 0; 507 tv.tv_usec = 500000; /* 500 ms */ 508 if (0 >= select (max + 1, &to_check, NULL, NULL, &tv)) 509 { 510 /* an error or timeout -> something's wrong or all plugins hung up */ 511 closed_channel = 0; 512 for (i = 0; i<num_channels; i++) 513 { 514 channel = channels[i]; 515 if (NULL == channel) 516 continue; 517 if (-1 == channel->plugin->seek_request) 518 { 519 /* plugin blocked for too long, kill channel */ 520 LOG ("Channel blocked, closing channel to %s\n", 521 channel->plugin->libname); 522 channel->plugin->channel = NULL; 523 channel->plugin->round_finished = 1; 524 EXTRACTOR_IPC_channel_destroy_ (channel); 525 channels[i] = NULL; 526 closed_channel = 1; 527 } 528 } 529 if (1 == closed_channel) 530 return 1; 531 /* strange, no channel is to blame, let's die just to be safe */ 532 if ((EINTR != errno) && (0 != errno)) 533 LOG_STRERROR ("select"); 534 return -1; 535 } 536 for (i = 0; i<num_channels; i++) 537 { 538 channel = channels[i]; 539 if (NULL == channel) 540 continue; 541 if (! FD_ISSET (channel->cpipe_out, &to_check)) 542 continue; 543 if (channel->mdata_size == channel->size) 544 { 545 /* not enough space, need to grow allocation (if allowed) */ 546 if (MAX_META_DATA == channel->mdata_size) 547 { 548 LOG ("Inbound message from channel too large, aborting\n"); 549 channel->plugin->channel = NULL; 550 channel->plugin->round_finished = 1; 551 EXTRACTOR_IPC_channel_destroy_ (channel); 552 channels[i] = NULL; 553 continue; 554 } 555 channel->mdata_size *= 2; 556 if (channel->mdata_size > MAX_META_DATA) 557 channel->mdata_size = MAX_META_DATA; 558 if (NULL == (ndata = realloc (channel->mdata, 559 channel->mdata_size))) 560 { 561 LOG_STRERROR ("realloc"); 562 channel->plugin->channel = NULL; 563 channel->plugin->round_finished = 1; 564 EXTRACTOR_IPC_channel_destroy_ (channel); 565 channels[i] = NULL; 566 continue; 567 } 568 channel->mdata = ndata; 569 } 570 do 571 { 572 iret = read (channel->cpipe_out, 573 &channel->mdata[channel->size], 574 channel->mdata_size - channel->size); 575 } 576 while ((-1 == iret) && (EINTR == errno)); 577 if ( (-1 == iret) || 578 (0 == iret) || 579 (-1 == (ret = EXTRACTOR_IPC_process_reply_ (channel->plugin, 580 channel->mdata, 581 channel->size + iret, 582 proc, proc_cls)) ) ) 583 { 584 if (-1 == iret) 585 LOG_STRERROR ("read"); 586 LOG ("Read error from channel, closing channel %s\n", 587 channel->plugin->libname); 588 /* The plugin is gone -- a crash in a plugin is exactly what this 589 library is built to survive. Forget the channel on the plugin 590 as well, or the next file would be handed to a 591 `struct EXTRACTOR_Channel' that has already been freed. */ 592 channel->plugin->channel = NULL; 593 channel->plugin->round_finished = 1; 594 EXTRACTOR_IPC_channel_destroy_ (channel); 595 channels[i] = NULL; 596 continue; 597 } 598 else 599 { 600 channel->size = channel->size + iret - ret; 601 memmove (channel->mdata, 602 &channel->mdata[ret], 603 channel->size); 604 } 605 } 606 return 1; 607 } 608 609 610 /* end of extractor_ipc_gnu.c */