taler-merchant-report-generator.c (26302B)
1 /* 2 This file is part of TALER 3 (C) 2025 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU Affero General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 13 You should have received a copy of the GNU General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 17 /** 18 * @file src/backend/taler-merchant-report-generator.c 19 * @brief Service for fetching and transmitting merchant reports 20 * @author Christian Grothoff 21 */ 22 #include "platform.h" 23 #include <gnunet/gnunet_util_lib.h> 24 #include <gnunet/gnunet_db_lib.h> 25 #include <gnunet/gnunet_curl_lib.h> 26 #include <taler/taler_merchant_util.h> 27 #include <taler/taler_curl_lib.h> 28 #include <taler/taler_dbevents.h> 29 #include <taler/taler_error_codes.h> 30 #include "merchantdb_lib.h" 31 #include "merchantdb_lib.h" 32 #include "taler/taler_merchant_service.h" 33 #include <microhttpd.h> 34 #include <curl/curl.h> 35 #include "merchant-database/delete_report.h" 36 #include "merchant-database/lookup_reports_pending.h" 37 #include "merchant-database/update_report_status.h" 38 #include "merchant-database/set_instance.h" 39 #include "merchant-database/event_listen.h" 40 41 42 /** 43 * Information about an active reporting activity. 44 */ 45 struct ReportActivity 46 { 47 48 /** 49 * Kept in a DLL. 50 */ 51 struct ReportActivity *next; 52 53 /** 54 * Kept in a DLL. 55 */ 56 struct ReportActivity *prev; 57 58 /** 59 * Transmission program that is running. 60 */ 61 struct GNUNET_Process *proc; 62 63 /** 64 * Handle to wait for @e proc to terminate. 65 */ 66 struct GNUNET_ChildWaitHandle *cwh; 67 68 /** 69 * Minor context that holds body and headers. 70 */ 71 struct TALER_CURL_PostContext post_ctx; 72 73 /** 74 * CURL easy handle for the HTTP request. 75 */ 76 CURL *eh; 77 78 /** 79 * Job handle for the HTTP request. 80 */ 81 struct GNUNET_CURL_Job *job; 82 83 /** 84 * ID of the instance we are working on. 85 */ 86 char *instance_id; 87 88 /** 89 * URL where we request the report from. 90 */ 91 char *url; 92 93 /** 94 * Report program section. 95 */ 96 char *report_program_section; 97 98 /** 99 * Report description. 100 */ 101 char *report_description; 102 103 /** 104 * Target address for transmission. 105 */ 106 char *target_address; 107 108 /** 109 * MIME type of the report. 110 */ 111 char *mime_type; 112 113 /** 114 * Report we are working on. 115 */ 116 uint64_t report_id; 117 118 /** 119 * Next transmission time, already calculated. 120 */ 121 struct GNUNET_TIME_Absolute next_transmission; 122 123 /** 124 * HTTP response code. 125 */ 126 long response_code; 127 128 /** 129 * Set to true if this is a one-shot report. 130 */ 131 bool one_shot; 132 133 }; 134 135 136 /** 137 * Global return value. 138 */ 139 static int global_ret; 140 141 /** 142 * #GNUNET_YES if we are in test mode and should exit when idle. 143 */ 144 static int test_mode; 145 146 /** 147 * Base URL of the merchant backend. 148 */ 149 static char *base_url; 150 151 /** 152 * Our configuration. 153 */ 154 static const struct GNUNET_CONFIGURATION_Handle *cfg; 155 156 /** 157 * Database connection. 158 */ 159 static struct TALER_MERCHANTDB_PostgresContext *pg; 160 161 /** 162 * Event handler for database change notifications. 163 */ 164 static struct GNUNET_DB_EventHandler *eh; 165 166 /** 167 * Task for checking pending reports. 168 */ 169 static struct GNUNET_SCHEDULER_Task *report_task; 170 171 /** 172 * When is the current report_task scheduled to run? 173 */ 174 static struct GNUNET_TIME_Absolute report_task_due; 175 176 /** 177 * Context for CURL operations. 178 */ 179 static struct GNUNET_CURL_Context *curl_ctx; 180 181 /** 182 * Reschedule context for CURL. 183 */ 184 static struct GNUNET_CURL_RescheduleContext *curl_rc; 185 186 /** 187 * Head of DLL of active report activities. 188 */ 189 static struct ReportActivity *ra_head; 190 191 /** 192 * Tail of DLL of active report activities. 193 */ 194 static struct ReportActivity *ra_tail; 195 196 197 /** 198 * Free a report activity structure. 199 * 200 * @param[in] ra report activity to free 201 */ 202 static void 203 free_ra (struct ReportActivity *ra) 204 { 205 if (NULL != ra->cwh) 206 { 207 GNUNET_wait_child_cancel (ra->cwh); 208 ra->cwh = NULL; 209 } 210 if (NULL != ra->proc) 211 { 212 GNUNET_break (GNUNET_OK == 213 GNUNET_process_kill (ra->proc, 214 SIGKILL)); 215 GNUNET_break (GNUNET_OK == 216 GNUNET_process_wait (ra->proc, 217 true, 218 NULL, 219 NULL)); 220 GNUNET_process_destroy (ra->proc); 221 ra->proc = NULL; 222 } 223 TALER_curl_easy_post_finished (&ra->post_ctx); 224 if (NULL != ra->eh) 225 { 226 curl_easy_cleanup (ra->eh); 227 ra->eh = NULL; 228 } 229 if (NULL != ra->job) 230 { 231 GNUNET_CURL_job_cancel (ra->job); 232 ra->job = NULL; 233 } 234 GNUNET_CONTAINER_DLL_remove (ra_head, 235 ra_tail, 236 ra); 237 GNUNET_free (ra->instance_id); 238 GNUNET_free (ra->report_program_section); 239 GNUNET_free (ra->report_description); 240 GNUNET_free (ra->target_address); 241 GNUNET_free (ra->mime_type); 242 GNUNET_free (ra->url); 243 GNUNET_free (ra); 244 } 245 246 247 /** 248 * Check for pending reports and process them. 249 * 250 * @param cls closure (unused) 251 */ 252 static void 253 check_pending_reports (void *cls); 254 255 256 /** 257 * Finish transmission of a report and update database. 258 * 259 * @param[in] ra report activity to finish 260 * @param ec error code (#TALER_EC_NONE on success) 261 * @param error_details human-readable error details (NULL on success) 262 */ 263 static void 264 finish_transmission (struct ReportActivity *ra, 265 enum TALER_ErrorCode ec, 266 const char *error_details) 267 { 268 enum GNUNET_DB_QueryStatus qs; 269 struct GNUNET_TIME_Timestamp next_ts; 270 271 next_ts = GNUNET_TIME_absolute_to_timestamp (ra->next_transmission); 272 qs = TALER_MERCHANTDB_set_instance (pg, 273 ra->instance_id); 274 if (qs <= 0) 275 { 276 free_ra (ra); 277 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 278 "Failed to set instance to update report status: %d\n", 279 qs); 280 global_ret = EXIT_FAILURE; 281 GNUNET_SCHEDULER_shutdown (); 282 return; 283 } 284 if ( (TALER_EC_NONE == ec) && 285 (ra->one_shot) ) 286 { 287 qs = TALER_MERCHANTDB_delete_report (pg, 288 ra->instance_id, 289 ra->report_id); 290 } 291 else 292 { 293 qs = TALER_MERCHANTDB_update_report_status (pg, 294 ra->instance_id, 295 ra->report_id, 296 next_ts, 297 ec, 298 error_details); 299 } 300 if (qs < 0) 301 { 302 free_ra (ra); 303 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 304 "Failed to update report status: %d\n", 305 qs); 306 global_ret = EXIT_FAILURE; 307 GNUNET_SCHEDULER_shutdown (); 308 return; 309 } 310 if ( (NULL == report_task) || 311 (GNUNET_TIME_absolute_cmp (report_task_due, 312 >, 313 ra->next_transmission)) ) 314 { 315 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 316 "Scheduling next report for %s\n", 317 GNUNET_TIME_absolute2s (ra->next_transmission)); 318 if (NULL != report_task) 319 GNUNET_SCHEDULER_cancel (report_task); 320 report_task_due = ra->next_transmission; 321 report_task = GNUNET_SCHEDULER_add_at (ra->next_transmission, 322 &check_pending_reports, 323 NULL); 324 } 325 free_ra (ra); 326 if (test_mode && 327 GNUNET_TIME_absolute_is_future (report_task_due) && 328 (NULL == ra_head)) 329 { 330 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 331 "Test mode, exiting because of going idle\n"); 332 GNUNET_SCHEDULER_shutdown (); 333 return; 334 } 335 } 336 337 338 /** 339 * Callback invoked when the child process terminates. 340 * 341 * @param cls closure, a `struct ReportActivity *` 342 * @param type type of the process 343 * @param exit_code exit code of the process 344 */ 345 static void 346 child_completed_cb (void *cls, 347 enum GNUNET_OS_ProcessStatusType type, 348 long unsigned int exit_code) 349 { 350 struct ReportActivity *ra = cls; 351 enum TALER_ErrorCode ec; 352 char *error_details = NULL; 353 354 ra->cwh = NULL; 355 GNUNET_process_destroy (ra->proc); 356 ra->proc = NULL; 357 if ( (GNUNET_OS_PROCESS_EXITED != type) || 358 (0 != exit_code) ) 359 { 360 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 361 "Report transmission program failed with status %d/%lu\n", 362 (int) type, 363 exit_code); 364 ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 365 GNUNET_asprintf (&error_details, 366 "Report transmission program exited with status %d/%lu", 367 (int) type, 368 exit_code); 369 } 370 else 371 { 372 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 373 "Report transmitted successfully\n"); 374 ec = TALER_EC_NONE; 375 } 376 finish_transmission (ra, 377 ec, 378 error_details); 379 GNUNET_free (error_details); 380 } 381 382 383 /** 384 * Transmit a report using the respective report program. 385 * 386 * @param[in,out] ra which report activity are we working on 387 * @param report_len length of @a report 388 * @param report binary report data to transmit 389 */ 390 static void 391 transmit_report (struct ReportActivity *ra, 392 size_t report_len, 393 const void *report) 394 { 395 const char *binary; 396 struct GNUNET_DISK_FileHandle *stdin_handle; 397 398 { 399 char *section; 400 401 GNUNET_asprintf (§ion, 402 "report-generator-%s", 403 ra->report_program_section); 404 if (GNUNET_OK != 405 GNUNET_CONFIGURATION_get_value_string (cfg, 406 section, 407 "BINARY", 408 (char **) &binary)) 409 { 410 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 411 section, 412 "BINARY"); 413 finish_transmission (ra, 414 TALER_EC_MERCHANT_GENERIC_REPORT_GENERATOR_UNCONFIGURED, 415 section); 416 GNUNET_free (section); 417 return; 418 } 419 GNUNET_free (section); 420 } 421 422 { 423 struct GNUNET_DISK_PipeHandle *stdin_pipe; 424 425 stdin_pipe = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW); 426 if (NULL == stdin_pipe) 427 { 428 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, 429 "pipe"); 430 finish_transmission (ra, 431 TALER_EC_GENERIC_OS_RESOURCE_ALLOCATION_FAILURE, 432 "pipe"); 433 return; 434 } 435 436 ra->proc = GNUNET_process_create (GNUNET_OS_INHERIT_STD_ERR); 437 GNUNET_assert (GNUNET_OK == 438 GNUNET_process_set_options ( 439 ra->proc, 440 GNUNET_process_option_inherit_rpipe (stdin_pipe, 441 STDIN_FILENO))); 442 if (GNUNET_OK != 443 GNUNET_process_run_command_va (ra->proc, 444 binary, 445 binary, 446 "-d", 447 ra->report_description, 448 "-m", 449 ra->mime_type, 450 "-t", 451 ra->target_address, 452 NULL)) 453 { 454 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, 455 "exec", 456 binary); 457 GNUNET_process_destroy (ra->proc); 458 ra->proc = NULL; 459 GNUNET_DISK_pipe_close (stdin_pipe); 460 finish_transmission (ra, 461 TALER_EC_MERCHANT_REPORT_GENERATOR_FAILED, 462 "Could not execute report generator binary"); 463 return; 464 } 465 466 /* Write report data to stdin of child process */ 467 stdin_handle = GNUNET_DISK_pipe_detach_end (stdin_pipe, 468 GNUNET_DISK_PIPE_END_WRITE); 469 GNUNET_DISK_pipe_close (stdin_pipe); 470 } 471 472 { 473 size_t off = 0; 474 475 while (off < report_len) 476 { 477 ssize_t wrote; 478 479 wrote = GNUNET_DISK_file_write (stdin_handle, 480 report, 481 report_len); 482 if (wrote <= 0) 483 break; 484 off += (size_t) wrote; 485 } 486 GNUNET_DISK_file_close (stdin_handle); 487 488 if (off != report_len) 489 { 490 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 491 "Failed to write report data to child process stdin\n"); 492 finish_transmission (ra, 493 TALER_EC_MERCHANT_REPORT_GENERATOR_FAILED, 494 "Failed to write to transmission program"); 495 return; 496 } 497 } 498 499 /* Wait for child to complete */ 500 ra->cwh = GNUNET_wait_child (ra->proc, 501 &child_completed_cb, 502 ra); 503 } 504 505 506 /** 507 * Callback invoked when CURL request completes. 508 * 509 * @param cls closure, a `struct ReportActivity *` 510 * @param response_code HTTP response code 511 * @param body http body of the response 512 * @param body_size number of bytes in @a body 513 */ 514 static void 515 curl_completed_cb (void *cls, 516 long response_code, 517 const void *body, 518 size_t body_size) 519 { 520 struct ReportActivity *ra = cls; 521 522 ra->job = NULL; 523 ra->response_code = response_code; 524 if (MHD_HTTP_OK != response_code) 525 { 526 char *error_details; 527 528 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 529 "Failed to fetch report data: HTTP %ld\n", 530 response_code); 531 GNUNET_asprintf (&error_details, 532 "HTTP request failed with status %ld from `%s'", 533 response_code, 534 ra->url); 535 finish_transmission (ra, 536 TALER_EC_MERCHANT_REPORT_FETCH_FAILED, 537 error_details); 538 GNUNET_free (error_details); 539 return; 540 } 541 transmit_report (ra, 542 body_size, 543 body); 544 } 545 546 547 /** 548 * Function to fetch data from @a data_source at @a instance_id 549 * and to send it to the @a target_address 550 * 551 * @param[in,out] ra which report activity are we working on 552 * @param mime_type mime type to request from @a data_source 553 * @param report_token token to get access to the report 554 */ 555 static void 556 fetch_and_transmit ( 557 struct ReportActivity *ra, 558 const char *mime_type, 559 const struct TALER_MERCHANT_ReportToken *report_token) 560 { 561 GNUNET_asprintf (&ra->url, 562 "%sreports/%llu", 563 base_url, 564 (unsigned long long) ra->report_id); 565 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 566 "Fetching report from %s\n", 567 ra->url); 568 ra->eh = curl_easy_init (); 569 if (NULL == ra->eh) 570 { 571 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 572 "Failed to initialize CURL handle\n"); 573 finish_transmission (ra, 574 TALER_EC_GENERIC_CURL_ALLOCATION_FAILURE, 575 "curl_easy_init"); 576 return; 577 } 578 579 { 580 char *accept_header; 581 582 GNUNET_asprintf (&accept_header, 583 "Accept: %s", 584 mime_type); 585 ra->post_ctx.headers = curl_slist_append (ra->post_ctx.headers, 586 accept_header); 587 GNUNET_free (accept_header); 588 } 589 GNUNET_assert (CURLE_OK == 590 curl_easy_setopt (ra->eh, 591 CURLOPT_URL, 592 ra->url)); 593 { 594 json_t *req; 595 596 req = GNUNET_JSON_PACK ( 597 GNUNET_JSON_pack_data_auto ("report_token", 598 report_token)); 599 if (GNUNET_OK != 600 TALER_curl_easy_post (&ra->post_ctx, 601 ra->eh, 602 req)) 603 { 604 GNUNET_break (0); 605 json_decref (req); 606 finish_transmission (ra, 607 TALER_EC_GENERIC_CURL_ALLOCATION_FAILURE, 608 "TALER_curl_easy_post"); 609 return; 610 } 611 json_decref (req); 612 } 613 ra->job = GNUNET_CURL_job_add_raw (curl_ctx, 614 ra->eh, 615 ra->post_ctx.headers, 616 &curl_completed_cb, 617 ra); 618 ra->eh = NULL; 619 } 620 621 622 /** 623 * Callback invoked for each pending report. 624 * 625 * @param cls closure 626 * @param instance_id name of the instance 627 * @param report_id serial number of the report 628 * @param report_program_section configuration section of program 629 * for report generation 630 * @param report_description text describing the report 631 * @param mime_type mime type to request 632 * @param report_token token to authorize access to the data source 633 * @param target_address where to send report data 634 * @param frequency report frequency 635 * @param frequency_shift how much to shift the report time from a 636 * multiple of the report @a frequency 637 * @param next_transmission when is the next transmission of this report 638 * due 639 * @param one_shot true if the report should be removed from the 640 * list after generation instead of being repeated 641 */ 642 static void 643 process_pending_report ( 644 void *cls, 645 const char *instance_id, 646 uint64_t report_id, 647 const char *report_program_section, 648 const char *report_description, 649 const char *mime_type, 650 const struct TALER_MERCHANT_ReportToken *report_token, 651 const char *target_address, 652 struct GNUNET_TIME_Relative frequency, 653 struct GNUNET_TIME_Relative frequency_shift, 654 struct GNUNET_TIME_Absolute next_transmission, 655 bool one_shot) 656 { 657 struct GNUNET_TIME_Absolute *next = cls; 658 struct ReportActivity *ra; 659 660 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 661 "Next report %llu is pending at %s\n", 662 (unsigned long long) report_id, 663 GNUNET_TIME_absolute2s (next_transmission)); 664 *next = next_transmission; 665 if (GNUNET_TIME_absolute_is_future (next_transmission)) 666 return; 667 *next = GNUNET_TIME_UNIT_ZERO_ABS; /* there might be more! */ 668 if ( (one_shot) || 669 (GNUNET_TIME_relative_is_zero (frequency)) ) 670 { 671 next_transmission = GNUNET_TIME_UNIT_FOREVER_ABS; 672 } 673 else 674 { 675 next_transmission = 676 GNUNET_TIME_absolute_add ( 677 GNUNET_TIME_absolute_round_down (GNUNET_TIME_absolute_get (), 678 frequency), 679 GNUNET_TIME_relative_add (frequency, 680 frequency_shift)); 681 } 682 if (! GNUNET_TIME_absolute_is_future (next_transmission)) 683 { 684 /* frequency near-zero!? */ 685 GNUNET_break (0); 686 next_transmission = GNUNET_TIME_relative_to_absolute ( 687 GNUNET_TIME_UNIT_MINUTES); 688 } 689 ra = GNUNET_new (struct ReportActivity); 690 ra->instance_id = GNUNET_strdup (instance_id); 691 ra->report_id = report_id; 692 ra->next_transmission = next_transmission; 693 ra->report_program_section = GNUNET_strdup (report_program_section); 694 ra->report_description = GNUNET_strdup (report_description); 695 ra->target_address = GNUNET_strdup (target_address); 696 ra->mime_type = GNUNET_strdup (mime_type); 697 ra->one_shot = one_shot; 698 GNUNET_CONTAINER_DLL_insert (ra_head, 699 ra_tail, 700 ra); 701 fetch_and_transmit (ra, 702 mime_type, 703 report_token); 704 } 705 706 707 static void 708 check_pending_reports (void *cls) 709 { 710 enum GNUNET_DB_QueryStatus qs; 711 struct GNUNET_TIME_Absolute next; 712 713 (void) cls; 714 report_task = NULL; 715 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 716 "Checking for pending reports...\n"); 717 next = GNUNET_TIME_UNIT_FOREVER_ABS; 718 qs = TALER_MERCHANTDB_lookup_reports_pending (pg, 719 &process_pending_report, 720 &next); 721 if (qs < 0) 722 { 723 GNUNET_break (0); 724 global_ret = EXIT_FAILURE; 725 GNUNET_SCHEDULER_shutdown (); 726 return; 727 } 728 if (NULL != ra_head) 729 return; /* wait for completion */ 730 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 731 "Found %d reports pending, next at %s\n", 732 (int) qs, 733 GNUNET_TIME_absolute2s (next)); 734 GNUNET_assert (NULL == report_task); 735 if (test_mode && 736 GNUNET_TIME_absolute_is_future (next) && 737 (NULL == ra_head)) 738 { 739 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 740 "Test mode, existing because of going idle\n"); 741 GNUNET_SCHEDULER_shutdown (); 742 return; 743 } 744 report_task_due = next; 745 report_task = GNUNET_SCHEDULER_add_at (next, 746 &check_pending_reports, 747 NULL); 748 } 749 750 751 /** 752 * Callback invoked when a MERCHANT_REPORT_UPDATE event is received. 753 * 754 * @param cls closure (unused) 755 * @param extra additional event data (unused) 756 * @param extra_size size of @a extra 757 */ 758 static void 759 report_update_cb (void *cls, 760 const void *extra, 761 size_t extra_size) 762 { 763 (void) cls; 764 (void) extra; 765 (void) extra_size; 766 767 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 768 "Received MERCHANT_REPORT_UPDATE event\n"); 769 /* Cancel any pending check and schedule immediate execution */ 770 if (NULL != report_task) 771 GNUNET_SCHEDULER_cancel (report_task); 772 report_task_due = GNUNET_TIME_UNIT_ZERO_ABS; 773 report_task = GNUNET_SCHEDULER_add_now (&check_pending_reports, 774 NULL); 775 } 776 777 778 /** 779 * Shutdown the service cleanly. 780 * 781 * @param cls closure (unused) 782 */ 783 static void 784 do_shutdown (void *cls) 785 { 786 struct ReportActivity *ra; 787 788 (void) cls; 789 790 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 791 "Shutting down report generator service\n"); 792 793 while (NULL != (ra = ra_head)) 794 free_ra (ra); 795 796 if (NULL != report_task) 797 { 798 GNUNET_SCHEDULER_cancel (report_task); 799 report_task = NULL; 800 } 801 if (NULL != curl_rc) 802 { 803 GNUNET_CURL_gnunet_rc_destroy (curl_rc); 804 curl_rc = NULL; 805 } 806 if (NULL != curl_ctx) 807 { 808 GNUNET_CURL_fini (curl_ctx); 809 curl_ctx = NULL; 810 } 811 if (NULL != eh) 812 { 813 TALER_MERCHANTDB_event_listen_cancel (eh); 814 eh = NULL; 815 } 816 if (NULL != pg) 817 { 818 TALER_MERCHANTDB_disconnect (pg); 819 pg = NULL; 820 } 821 GNUNET_free (base_url); 822 base_url = NULL; 823 } 824 825 826 /** 827 * Main function for the report generator service. 828 * 829 * @param cls closure 830 * @param args remaining command-line arguments 831 * @param cfgfile name of the configuration file used 832 * @param config configuration 833 */ 834 static void 835 run (void *cls, 836 char *const *args, 837 const char *cfgfile, 838 const struct GNUNET_CONFIGURATION_Handle *config) 839 { 840 (void) cls; 841 (void) args; 842 (void) cfgfile; 843 844 cfg = config; 845 if (GNUNET_OK != 846 GNUNET_CONFIGURATION_get_value_string (cfg, 847 "merchant", 848 "BASE_URL", 849 &base_url)) 850 { 851 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 852 "merchant", 853 "BASE_URL"); 854 global_ret = EXIT_NOTCONFIGURED; 855 return; 856 } 857 if (! TALER_is_web_url (base_url)) 858 { 859 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 860 "merchant", 861 "BASE_URL", 862 "Not a Web URL"); 863 global_ret = EXIT_NOTCONFIGURED; 864 return; 865 } 866 867 /* Ensure base_url ends with '/' */ 868 if ('/' != base_url[strlen (base_url) - 1]) 869 { 870 char *tmp; 871 872 GNUNET_asprintf (&tmp, 873 "%s/", 874 base_url); 875 GNUNET_free (base_url); 876 base_url = tmp; 877 } 878 879 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 880 NULL); 881 882 curl_ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 883 &curl_rc); 884 if (NULL == curl_ctx) 885 { 886 GNUNET_break (0); 887 global_ret = EXIT_FAILURE; 888 GNUNET_SCHEDULER_shutdown (); 889 return; 890 } 891 curl_rc = GNUNET_CURL_gnunet_rc_create (curl_ctx); 892 893 pg = TALER_MERCHANTDB_connect (cfg); 894 if (NULL == pg) 895 { 896 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 897 "Failed to connect to database. Consider running taler-merchant-dbconfig!\n"); 898 global_ret = EXIT_NOTINSTALLED; 899 GNUNET_SCHEDULER_shutdown (); 900 return; 901 } 902 { 903 struct GNUNET_DB_EventHeaderP ev = { 904 .size = htons (sizeof (ev)), 905 .type = htons (TALER_DBEVENT_MERCHANT_REPORT_UPDATE) 906 }; 907 908 eh = TALER_MERCHANTDB_event_listen (pg, 909 &ev, 910 GNUNET_TIME_UNIT_FOREVER_REL, 911 &report_update_cb, 912 NULL); 913 if (NULL == eh) 914 { 915 GNUNET_break (0); 916 global_ret = EXIT_FAILURE; 917 GNUNET_SCHEDULER_shutdown (); 918 return; 919 } 920 } 921 report_task = GNUNET_SCHEDULER_add_now (&check_pending_reports, 922 NULL); 923 } 924 925 926 /** 927 * The main function of the report generator service. 928 * 929 * @param argc number of arguments from the command line 930 * @param argv command line arguments 931 * @return 0 ok, 1 on error 932 */ 933 int 934 main (int argc, 935 char *const *argv) 936 { 937 struct GNUNET_GETOPT_CommandLineOption options[] = { 938 GNUNET_GETOPT_option_flag ('t', 939 "test", 940 "run in test mode and exit when idle", 941 &test_mode), 942 GNUNET_GETOPT_option_timetravel ('T', 943 "timetravel"), 944 GNUNET_GETOPT_option_version (VERSION), 945 GNUNET_GETOPT_OPTION_END 946 }; 947 enum GNUNET_GenericReturnValue ret; 948 949 ret = GNUNET_PROGRAM_run ( 950 TALER_MERCHANT_project_data (), 951 argc, argv, 952 "taler-merchant-report-generator", 953 "Fetch and transmit periodic merchant reports", 954 options, 955 &run, 956 NULL); 957 if (GNUNET_SYSERR == ret) 958 return EXIT_INVALIDARGUMENT; 959 if (GNUNET_NO == ret) 960 return EXIT_SUCCESS; 961 return global_ret; 962 } 963 964 965 /* end of taler-merchant-report-generator.c */