extractor_ipc_gnu.c (15397B)
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 const char *tpath; 138 139 if (NULL == (shm = malloc (sizeof (struct EXTRACTOR_SharedMemory)))) 140 { 141 LOG_STRERROR ("malloc"); 142 return NULL; 143 } 144 #if SOMEBSD 145 /* this works on FreeBSD, not sure about others... */ 146 tpath = getenv ("TMPDIR"); 147 if (NULL == tpath) 148 tpath = "/tmp/"; 149 #else 150 tpath = "/"; /* Linux */ 151 #endif 152 snprintf (shm->shm_name, 153 MAX_SHM_NAME, 154 "%sLE-%u-%u", 155 tpath, 156 getpid (), 157 (unsigned int) random ()); 158 if (-1 == (shm->shm_id = shm_open (shm->shm_name, 159 O_RDWR | O_CREAT, 160 S_IRUSR | S_IWUSR))) 161 { 162 LOG_STRERROR_FILE ("shm_open", 163 shm->shm_name); 164 free (shm); 165 return NULL; 166 } 167 if ( (0 != ftruncate (shm->shm_id, size)) || 168 (NULL == (shm->shm_ptr = mmap (NULL, 169 size, 170 PROT_WRITE, 171 MAP_SHARED, 172 shm->shm_id, 173 0))) || 174 (((void*) -1) == shm->shm_ptr) ) 175 { 176 LOG_STRERROR ("ftruncate/mmap"); 177 (void) close (shm->shm_id); 178 (void) shm_unlink (shm->shm_name); 179 free (shm); 180 return NULL; 181 } 182 shm->shm_size = size; 183 shm->rc = 0; 184 return shm; 185 } 186 187 188 /** 189 * Change the reference counter for this shm instance. 190 * 191 * @param shm instance to update 192 * @param delta value to change RC by 193 * @return new RC 194 */ 195 unsigned int 196 EXTRACTOR_IPC_shared_memory_change_rc_ (struct EXTRACTOR_SharedMemory *shm, 197 int delta) 198 { 199 shm->rc += delta; 200 return shm->rc; 201 } 202 203 204 /** 205 * Destroy shared memory area. 206 * 207 * @param shm memory area to destroy 208 * @return NULL on error 209 */ 210 void 211 EXTRACTOR_IPC_shared_memory_destroy_ (struct EXTRACTOR_SharedMemory *shm) 212 { 213 munmap (shm->shm_ptr, 214 shm->shm_size); 215 (void) close (shm->shm_id); 216 (void) shm_unlink (shm->shm_name); 217 free (shm); 218 } 219 220 221 /** 222 * Initialize shared memory area from data source. 223 * 224 * @param shm memory area to initialize 225 * @param ds data source to use for initialization 226 * @param off offset to use in data source 227 * @param size number of bytes to copy 228 * @return -1 on error, otherwise number of bytes copied 229 */ 230 ssize_t 231 EXTRACTOR_IPC_shared_memory_set_ (struct EXTRACTOR_SharedMemory *shm, 232 struct EXTRACTOR_Datasource *ds, 233 uint64_t off, 234 size_t size) 235 { 236 if (-1 == 237 EXTRACTOR_datasource_seek_ (ds, 238 off, 239 SEEK_SET)) 240 { 241 LOG ("Failed to set IPC memory due to seek error\n"); 242 return -1; 243 } 244 if (size > shm->shm_size) 245 size = shm->shm_size; 246 return EXTRACTOR_datasource_read_ (ds, 247 shm->shm_ptr, 248 size); 249 } 250 251 252 /** 253 * Query datasource for current position 254 * 255 * @param ds data source to query 256 * @return current position in the datasource or UINT64_MAX on error 257 */ 258 uint64_t 259 EXTRACTOR_datasource_get_pos_ (struct EXTRACTOR_Datasource *ds) 260 { 261 int64_t pos = EXTRACTOR_datasource_seek_ (ds, 262 0, 263 SEEK_CUR); 264 265 if (-1 == pos) 266 return UINT64_MAX; 267 return pos; 268 } 269 270 271 /** 272 * Create a channel to communicate with a process wrapping 273 * the plugin of the given name. Starts the process as well. 274 * 275 * @param plugin the plugin 276 * @param shm memory to share with the process 277 * @return NULL on error, otherwise IPC channel 278 */ 279 struct EXTRACTOR_Channel * 280 EXTRACTOR_IPC_channel_create_ (struct EXTRACTOR_PluginList *plugin, 281 struct EXTRACTOR_SharedMemory *shm) 282 { 283 struct EXTRACTOR_Channel *channel; 284 int p1[2]; 285 int p2[2]; 286 pid_t pid; 287 struct InitMessage *init; 288 size_t slen; 289 static int sigpipe_ignored; 290 291 if (! sigpipe_ignored) 292 { 293 /* The cpipe_in end may be written to after the plugin child 294 has died; without ignoring SIGPIPE this would terminate the 295 host application. As the channel uses pipes (not sockets), 296 MSG_NOSIGNAL is not applicable, so we ignore SIGPIPE here. */ 297 signal (SIGPIPE, SIG_IGN); 298 sigpipe_ignored = 1; 299 } 300 if (NULL == (channel = malloc (sizeof (struct EXTRACTOR_Channel)))) 301 { 302 LOG_STRERROR ("malloc"); 303 return NULL; 304 } 305 channel->mdata_size = 1024; 306 if (NULL == (channel->mdata = malloc (channel->mdata_size))) 307 { 308 LOG_STRERROR ("malloc"); 309 free (channel); 310 return NULL; 311 } 312 channel->shm = shm; 313 channel->plugin = plugin; 314 channel->size = 0; 315 if (0 != pipe (p1)) 316 { 317 LOG_STRERROR ("pipe"); 318 free (channel->mdata); 319 free (channel); 320 return NULL; 321 } 322 if (0 != pipe (p2)) 323 { 324 LOG_STRERROR ("pipe"); 325 (void) close (p1[0]); 326 (void) close (p1[1]); 327 free (channel->mdata); 328 free (channel); 329 return NULL; 330 } 331 pid = fork (); 332 if (pid == -1) 333 { 334 LOG_STRERROR ("fork"); 335 (void) close (p1[0]); 336 (void) close (p1[1]); 337 (void) close (p2[0]); 338 (void) close (p2[1]); 339 free (channel->mdata); 340 free (channel); 341 return NULL; 342 } 343 if (0 == pid) 344 { 345 (void) close (p1[1]); 346 (void) close (p2[0]); 347 free (channel->mdata); 348 free (channel); 349 #if HAVE_SYS_APPARMOR_H 350 #if HAVE_APPARMOR 351 if (0 > aa_change_profile ("libextractor")) 352 { 353 int eno = errno; 354 355 if ( (EINVAL != eno) && 356 (ENOENT != eno) ) 357 { 358 fprintf (stderr, 359 "Failure changing AppArmor profile: %s\n", 360 strerror (errno)); 361 _exit (1); 362 } 363 } 364 #endif 365 #endif 366 EXTRACTOR_plugin_main_ (plugin, p1[0], p2[1]); 367 _exit (0); 368 } 369 (void) close (p1[0]); 370 (void) close (p2[1]); 371 channel->cpipe_in = p1[1]; 372 channel->cpipe_out = p2[0]; 373 channel->cpid = pid; 374 slen = strlen (shm->shm_name) + 1; 375 if (NULL == (init = malloc (sizeof (struct InitMessage) + slen))) 376 { 377 LOG_STRERROR ("malloc"); 378 EXTRACTOR_IPC_channel_destroy_ (channel); 379 return NULL; 380 } 381 init->opcode = MESSAGE_INIT_STATE; 382 init->reserved = 0; 383 init->reserved2 = 0; 384 init->shm_name_length = slen; 385 init->shm_map_size = shm->shm_size; 386 memcpy (&init[1], shm->shm_name, slen); 387 if (sizeof (struct InitMessage) + slen != 388 EXTRACTOR_IPC_channel_send_ (channel, 389 init, 390 sizeof (struct InitMessage) + slen) ) 391 { 392 LOG ("Failed to send INIT_STATE message to plugin\n"); 393 EXTRACTOR_IPC_channel_destroy_ (channel); 394 free (init); 395 return NULL; 396 } 397 free (init); 398 return channel; 399 } 400 401 402 /** 403 * Destroy communication channel with a plugin/process. Also 404 * destroys the process. 405 * 406 * @param channel channel to communicate with the plugin 407 */ 408 void 409 EXTRACTOR_IPC_channel_destroy_ (struct EXTRACTOR_Channel *channel) 410 { 411 int status; 412 413 if (0 != kill (channel->cpid, SIGKILL)) 414 LOG_STRERROR ("kill"); 415 if (-1 == waitpid (channel->cpid, &status, 0)) 416 LOG_STRERROR ("waitpid"); 417 if (0 != close (channel->cpipe_out)) 418 LOG_STRERROR ("close"); 419 if (0 != close (channel->cpipe_in)) 420 LOG_STRERROR ("close"); 421 if (NULL != channel->plugin) 422 channel->plugin->channel = NULL; 423 free (channel->mdata); 424 free (channel); 425 } 426 427 428 /** 429 * Send data via the given IPC channel (blocking). 430 * 431 * @param channel channel to communicate with the plugin 432 * @param buf data to send 433 * @param size number of bytes in buf to send 434 * @return -1 on error, number of bytes sent on success 435 * (never does partial writes) 436 */ 437 ssize_t 438 EXTRACTOR_IPC_channel_send_ (struct EXTRACTOR_Channel *channel, 439 const void *data, 440 size_t size) 441 { 442 const char *cdata = data; 443 size_t off = 0; 444 ssize_t ret; 445 446 while (off < size) 447 { 448 ret = write (channel->cpipe_in, &cdata[off], size - off); 449 if (ret <= 0) 450 { 451 if ((-1 == ret) && (EINTR == errno)) 452 continue; 453 if (-1 == ret) 454 LOG_STRERROR ("write"); 455 return -1; 456 } 457 off += ret; 458 } 459 return size; 460 } 461 462 463 /** 464 * Receive data from any of the given IPC channels (blocking). 465 * Wait for one of the plugins to reply. 466 * Selects on plugin output pipes, runs 'receive_reply' 467 * on each activated pipe until it gets a seek request 468 * or a done message. Called repeatedly by the user until all pipes are dry or 469 * broken. 470 * 471 * @param channels array of channels, channels that break may be set to NULL 472 * @param num_channels length of the @a channels array 473 * @param proc function to call to process messages (may be called 474 * more than once) 475 * @param proc_cls closure for @a proc 476 * @return -1 on error, 1 on success 477 */ 478 int 479 EXTRACTOR_IPC_channel_recv_ (struct EXTRACTOR_Channel **channels, 480 unsigned int num_channels, 481 EXTRACTOR_ChannelMessageProcessor proc, 482 void *proc_cls) 483 { 484 struct timeval tv; 485 fd_set to_check; 486 int max; 487 unsigned int i; 488 struct EXTRACTOR_Channel *channel; 489 ssize_t ret; 490 ssize_t iret; 491 char *ndata; 492 int closed_channel; 493 494 FD_ZERO (&to_check); 495 max = -1; 496 for (i = 0; i<num_channels; i++) 497 { 498 channel = channels[i]; 499 if (NULL == channel) 500 continue; 501 FD_SET (channel->cpipe_out, &to_check); 502 if (max < channel->cpipe_out) 503 max = channel->cpipe_out; 504 } 505 if (-1 == max) 506 { 507 return 1; /* nothing left to do! */ 508 } 509 tv.tv_sec = 0; 510 tv.tv_usec = 500000; /* 500 ms */ 511 if (0 >= select (max + 1, &to_check, NULL, NULL, &tv)) 512 { 513 /* an error or timeout -> something's wrong or all plugins hung up */ 514 closed_channel = 0; 515 for (i = 0; i<num_channels; i++) 516 { 517 channel = channels[i]; 518 if (NULL == channel) 519 continue; 520 if (-1 == channel->plugin->seek_request) 521 { 522 /* plugin blocked for too long, kill channel */ 523 LOG ("Channel blocked, closing channel to %s\n", 524 channel->plugin->libname); 525 channel->plugin->channel = NULL; 526 channel->plugin->round_finished = 1; 527 EXTRACTOR_IPC_channel_destroy_ (channel); 528 channels[i] = NULL; 529 closed_channel = 1; 530 } 531 } 532 if (1 == closed_channel) 533 return 1; 534 /* strange, no channel is to blame, let's die just to be safe */ 535 if ((EINTR != errno) && (0 != errno)) 536 LOG_STRERROR ("select"); 537 return -1; 538 } 539 for (i = 0; i<num_channels; i++) 540 { 541 channel = channels[i]; 542 if (NULL == channel) 543 continue; 544 if (! FD_ISSET (channel->cpipe_out, &to_check)) 545 continue; 546 if (channel->mdata_size == channel->size) 547 { 548 /* not enough space, need to grow allocation (if allowed) */ 549 if (MAX_META_DATA == channel->mdata_size) 550 { 551 LOG ("Inbound message from channel too large, aborting\n"); 552 EXTRACTOR_IPC_channel_destroy_ (channel); 553 channels[i] = NULL; 554 continue; 555 } 556 channel->mdata_size *= 2; 557 if (channel->mdata_size > MAX_META_DATA) 558 channel->mdata_size = MAX_META_DATA; 559 if (NULL == (ndata = realloc (channel->mdata, 560 channel->mdata_size))) 561 { 562 LOG_STRERROR ("realloc"); 563 EXTRACTOR_IPC_channel_destroy_ (channel); 564 channels[i] = NULL; 565 continue; 566 } 567 channel->mdata = ndata; 568 } 569 do 570 { 571 iret = read (channel->cpipe_out, 572 &channel->mdata[channel->size], 573 channel->mdata_size - channel->size); 574 } 575 while ((-1 == iret) && (EINTR == errno)); 576 if ( (-1 == iret) || 577 (0 == iret) || 578 (-1 == (ret = EXTRACTOR_IPC_process_reply_ (channel->plugin, 579 channel->mdata, 580 channel->size + iret, 581 proc, proc_cls)) ) ) 582 { 583 if (-1 == iret) 584 LOG_STRERROR ("read"); 585 LOG ("Read error from channel, closing channel %s\n", 586 channel->plugin->libname); 587 EXTRACTOR_IPC_channel_destroy_ (channel); 588 channels[i] = NULL; 589 continue; 590 } 591 else 592 { 593 channel->size = channel->size + iret - ret; 594 memmove (channel->mdata, 595 &channel->mdata[ret], 596 channel->size); 597 } 598 } 599 return 1; 600 } 601 602 603 /* end of extractor_ipc_gnu.c */