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