LCOV - code coverage report
Current view: top level - source4/wrepl_server - wrepl_server.c (source / functions) Hit Total Coverage
Test: coverage report for abartlet/fix-coverage dd10fb34 Lines: 165 211 78.2 %
Date: 2021-09-23 10:06:22 Functions: 12 12 100.0 %

          Line data    Source code
       1             : /* 
       2             :    Unix SMB/CIFS implementation.
       3             :    
       4             :    WINS Replication server
       5             :    
       6             :    Copyright (C) Stefan Metzmacher      2005
       7             :    
       8             :    This program is free software; you can redistribute it and/or modify
       9             :    it under the terms of the GNU General Public License as published by
      10             :    the Free Software Foundation; either version 3 of the License, or
      11             :    (at your option) any later version.
      12             :    
      13             :    This program is distributed in the hope that it will be useful,
      14             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16             :    GNU General Public License for more details.
      17             :    
      18             :    You should have received a copy of the GNU General Public License
      19             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      20             : */
      21             : 
      22             : #include "includes.h"
      23             : #include "../lib/util/dlinklist.h"
      24             : #include "samba/service_task.h"
      25             : #include "samba/service.h"
      26             : #include "lib/messaging/irpc.h"
      27             : #include "librpc/gen_ndr/winsrepl.h"
      28             : #include "wrepl_server/wrepl_server.h"
      29             : #include "nbt_server/wins/winsdb.h"
      30             : #include <ldb.h>
      31             : #include <ldb_errors.h>
      32             : #include "auth/auth.h"
      33             : #include "ldb_wrap.h"
      34             : #include "param/param.h"
      35             : #include "lib/socket/netif.h"
      36             : 
      37          63 : static struct ldb_context *wins_config_db_connect(TALLOC_CTX *mem_ctx, 
      38             :                                                   struct tevent_context *ev_ctx,
      39             :                                                   struct loadparm_context *lp_ctx)
      40             : {
      41          63 :         return ldb_wrap_connect(mem_ctx, ev_ctx, lp_ctx, lpcfg_private_path(mem_ctx,
      42             :                                 lp_ctx, "wins_config.ldb"),
      43             :                                 system_session(lp_ctx), NULL, 0);
      44             : }
      45             : 
      46        5558 : static uint64_t wins_config_db_get_seqnumber(struct ldb_context *ldb)
      47             : {
      48             :         int ret;
      49             :         struct ldb_dn *dn;
      50        5558 :         struct ldb_result *res = NULL;
      51        5558 :         TALLOC_CTX *tmp_ctx = talloc_new(ldb);
      52        5558 :         uint64_t seqnumber = 0;
      53             : 
      54        5558 :         dn = ldb_dn_new(tmp_ctx, ldb, "@BASEINFO");
      55        5558 :         if (!dn) goto failed;
      56             : 
      57             :         /* find the record in the WINS database */
      58        5558 :         ret = ldb_search(ldb, tmp_ctx, &res, dn, LDB_SCOPE_BASE, NULL, NULL);
      59        5558 :         if (ret != LDB_SUCCESS) goto failed;
      60        5558 :         if (res->count > 1) goto failed;
      61             : 
      62        5558 :         if (res->count == 1) {
      63        5558 :                 seqnumber = ldb_msg_find_attr_as_uint64(res->msgs[0], "sequenceNumber", 0);
      64             :         }
      65             : 
      66        4101 : failed:
      67        5558 :         talloc_free(tmp_ctx);
      68        5558 :         return seqnumber;
      69             : }
      70             : 
      71             : /*
      72             :   open winsdb
      73             : */
      74          63 : static NTSTATUS wreplsrv_open_winsdb(struct wreplsrv_service *service, 
      75             :                                      struct loadparm_context *lp_ctx)
      76             : {
      77          63 :         const char *owner = lpcfg_parm_string(lp_ctx, NULL, "winsdb", "local_owner");
      78             : 
      79          63 :         if (owner == NULL) {
      80             :                 struct interface *ifaces;
      81          63 :                 load_interface_list(service, lp_ctx, &ifaces);
      82          63 :                 owner = iface_list_first_v4(ifaces);
      83             :         }
      84             : 
      85          63 :         service->wins_db     = winsdb_connect(service, service->task->event_ctx, lp_ctx, owner, WINSDB_HANDLE_CALLER_WREPL);
      86          63 :         if (!service->wins_db) {
      87           0 :                 return NT_STATUS_INTERNAL_DB_ERROR;
      88             :         }
      89             : 
      90          63 :         service->config.ldb = wins_config_db_connect(service, service->task->event_ctx, lp_ctx);
      91          63 :         if (!service->config.ldb) {
      92           0 :                 return NT_STATUS_INTERNAL_DB_ERROR;
      93             :         }
      94             : 
      95             :         /* the default renew interval is 6 days */
      96          63 :         service->config.renew_interval         = lpcfg_parm_int(lp_ctx, NULL,"wreplsrv","renew_interval", 6*24*60*60);
      97             : 
      98             :         /* the default tombstone (extinction) interval is 6 days */
      99          63 :         service->config.tombstone_interval= lpcfg_parm_int(lp_ctx, NULL,"wreplsrv","tombstone_interval", 6*24*60*60);
     100             : 
     101             :         /* the default tombstone (extinction) timeout is 1 day */
     102          63 :         service->config.tombstone_timeout = lpcfg_parm_int(lp_ctx, NULL,"wreplsrv","tombstone_timeout", 1*24*60*60);
     103             : 
     104             :         /* the default tombstone extra timeout is 3 days */
     105          63 :         service->config.tombstone_extra_timeout = lpcfg_parm_int(lp_ctx, NULL,"wreplsrv","tombstone_extra_timeout", 3*24*60*60);
     106             : 
     107             :         /* the default verify interval is 24 days */
     108          63 :         service->config.verify_interval   = lpcfg_parm_int(lp_ctx, NULL,"wreplsrv","verify_interval", 24*24*60*60);
     109             : 
     110             :         /* the default scavenging interval is 'renew_interval/2' */
     111          82 :         service->config.scavenging_interval=lpcfg_parm_int(lp_ctx, NULL,"wreplsrv","scavenging_interval",
     112          63 :                                                         service->config.renew_interval/2);
     113             : 
     114             :         /* the maximun interval to the next periodic processing event */
     115          63 :         service->config.periodic_interval = lpcfg_parm_int(lp_ctx, NULL,"wreplsrv","periodic_interval", 15);
     116             : 
     117          63 :         return NT_STATUS_OK;
     118             : }
     119             : 
     120         797 : struct wreplsrv_partner *wreplsrv_find_partner(struct wreplsrv_service *service, const char *peer_addr)
     121             : {
     122             :         struct wreplsrv_partner *cur;
     123             : 
     124         852 :         for (cur = service->partners; cur; cur = cur->next) {
     125         734 :                 if (strcmp(cur->address, peer_addr) == 0) {
     126         679 :                         return cur;
     127             :                 }
     128             :         }
     129             : 
     130         114 :         return NULL;
     131             : }
     132             : 
     133             : /*
     134             :   load our replication partners
     135             : */
     136        5558 : NTSTATUS wreplsrv_load_partners(struct wreplsrv_service *service)
     137             : {
     138             :         struct wreplsrv_partner *partner;
     139        5558 :         struct ldb_result *res = NULL;
     140             :         int ret;
     141             :         TALLOC_CTX *tmp_ctx;
     142             :         unsigned int i;
     143             :         uint64_t new_seqnumber;
     144             : 
     145        5558 :         new_seqnumber = wins_config_db_get_seqnumber(service->config.ldb);
     146             : 
     147             :         /* if it's not the first run and nothing changed we're done */
     148        5558 :         if (service->config.seqnumber != 0 && service->config.seqnumber == new_seqnumber) {
     149        5495 :                 return NT_STATUS_OK;
     150             :         }
     151             : 
     152          63 :         tmp_ctx = talloc_new(service);
     153          63 :         NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
     154             : 
     155          63 :         service->config.seqnumber = new_seqnumber;
     156             : 
     157             :         /* find the record in the WINS database */
     158          63 :         ret = ldb_search(service->config.ldb, tmp_ctx, &res,
     159             :                          ldb_dn_new(tmp_ctx, service->config.ldb, "CN=PARTNERS"),
     160             :                          LDB_SCOPE_SUBTREE, NULL, "(objectClass=wreplPartner)");
     161          63 :         if (ret != LDB_SUCCESS) goto failed;
     162             : 
     163             :         /* first disable all existing partners */
     164          63 :         for (partner=service->partners; partner; partner = partner->next) {
     165           0 :                 partner->type = WINSREPL_PARTNER_NONE;
     166             :         }
     167             : 
     168         110 :         for (i=0; i < res->count; i++) {
     169             :                 const char *address;
     170             : 
     171          49 :                 address = ldb_msg_find_attr_as_string(res->msgs[i], "address", NULL);
     172          49 :                 if (!address) {
     173           0 :                         goto failed;
     174             :                 }
     175             : 
     176          49 :                 partner = wreplsrv_find_partner(service, address);
     177          49 :                 if (partner) {
     178           0 :                         if (partner->name != partner->address) {
     179           0 :                                 talloc_free(discard_const(partner->name));
     180             :                         }
     181           0 :                         partner->name = NULL;
     182           0 :                         talloc_free(discard_const(partner->our_address));
     183           0 :                         partner->our_address = NULL;
     184             : 
     185             :                         /* force rescheduling of pulling */
     186           0 :                         partner->pull.next_run = timeval_zero();
     187             :                 } else {
     188          49 :                         partner = talloc_zero(service, struct wreplsrv_partner);
     189          49 :                         if (partner == NULL) goto failed;
     190             : 
     191          49 :                         partner->service = service;
     192          49 :                         partner->address = address;
     193          49 :                         talloc_steal(partner, partner->address);
     194             : 
     195          49 :                         DLIST_ADD_END(service->partners, partner);
     196             :                 }
     197             : 
     198          49 :                 partner->name                        = ldb_msg_find_attr_as_string(res->msgs[i], "name", partner->address);
     199          49 :                 talloc_steal(partner, partner->name);
     200          49 :                 partner->our_address         = ldb_msg_find_attr_as_string(res->msgs[i], "ourAddress", NULL);
     201          49 :                 talloc_steal(partner, partner->our_address);
     202             : 
     203          49 :                 partner->type                        = ldb_msg_find_attr_as_uint(res->msgs[i], "type", WINSREPL_PARTNER_BOTH);
     204          49 :                 partner->pull.interval               = ldb_msg_find_attr_as_uint(res->msgs[i], "pullInterval",
     205             :                                                                     WINSREPL_DEFAULT_PULL_INTERVAL);
     206          49 :                 partner->pull.retry_interval = ldb_msg_find_attr_as_uint(res->msgs[i], "pullRetryInterval",
     207             :                                                                     WINSREPL_DEFAULT_PULL_RETRY_INTERVAL);
     208          49 :                 partner->push.change_count   = ldb_msg_find_attr_as_uint(res->msgs[i], "pushChangeCount",
     209             :                                                                     WINSREPL_DEFAULT_PUSH_CHANGE_COUNT);
     210          49 :                 partner->push.use_inform     = ldb_msg_find_attr_as_uint(res->msgs[i], "pushUseInform", true);
     211             : 
     212          49 :                 DEBUG(3,("wreplsrv_load_partners: found partner: %s type: 0x%X\n",
     213             :                         partner->address, partner->type));
     214             :         }
     215             : 
     216          63 :         DEBUG(2,("wreplsrv_load_partners: %u partners found: wins_config_db seqnumber %llu\n",
     217             :                 res->count, (unsigned long long)service->config.seqnumber));
     218             : 
     219          63 :         talloc_free(tmp_ctx);
     220          63 :         return NT_STATUS_OK;
     221           0 : failed:
     222           0 :         talloc_free(tmp_ctx);
     223           0 :         return NT_STATUS_FOOBAR;
     224             : }
     225             : 
     226           3 : NTSTATUS wreplsrv_fill_wrepl_table(struct wreplsrv_service *service,
     227             :                                    TALLOC_CTX *mem_ctx,
     228             :                                    struct wrepl_table *table_out,
     229             :                                    const char *initiator,
     230             :                                    bool full_table)
     231             : {
     232             :         struct wreplsrv_owner *cur;
     233           3 :         uint32_t i = 0;
     234             : 
     235           3 :         table_out->partner_count     = 0;
     236           3 :         table_out->partners          = NULL;
     237           3 :         table_out->initiator         = initiator;
     238             : 
     239           9 :         for (cur = service->table; cur; cur = cur->next) {
     240           6 :                 if (full_table) {
     241           6 :                         table_out->partner_count++;
     242           6 :                         continue;
     243             :                 }
     244             : 
     245           0 :                 if (strcmp(initiator, cur->owner.address) != 0) continue;
     246             : 
     247           0 :                 table_out->partner_count++;
     248           0 :                 break;
     249             :         }
     250             : 
     251           3 :         table_out->partners = talloc_array(mem_ctx, struct wrepl_wins_owner, table_out->partner_count);
     252           3 :         NT_STATUS_HAVE_NO_MEMORY(table_out->partners);
     253             : 
     254           9 :         for (cur = service->table; cur && i < table_out->partner_count; cur = cur->next) {
     255             :                 /*
     256             :                  * if it's our local entry
     257             :                  * update the max version
     258             :                  */
     259           6 :                 if (cur == service->owner) {
     260           3 :                         cur->owner.max_version = winsdb_get_maxVersion(service->wins_db);
     261             :                 }
     262             : 
     263           6 :                 if (full_table) {
     264           6 :                         table_out->partners[i] = cur->owner;
     265           6 :                         i++;
     266           6 :                         continue;
     267             :                 }
     268             : 
     269           0 :                 if (strcmp(initiator, cur->owner.address) != 0) continue;
     270             : 
     271           0 :                 table_out->partners[i] = cur->owner;
     272           0 :                 i++;
     273           0 :                 break;
     274             :         }
     275             : 
     276           3 :         return NT_STATUS_OK;
     277             : }
     278             : 
     279        5019 : struct wreplsrv_owner *wreplsrv_find_owner(struct wreplsrv_service *service,
     280             :                                            struct wreplsrv_owner *table,
     281             :                                            const char *wins_owner)
     282             : {
     283             :         struct wreplsrv_owner *cur;
     284             : 
     285       10628 :         for (cur = table; cur; cur = cur->next) {
     286       10556 :                 if (strcmp(cur->owner.address, wins_owner) == 0) {
     287             :                         /*
     288             :                          * if it's our local entry
     289             :                          * update the max version
     290             :                          */
     291        4947 :                         if (cur == service->owner) {
     292          72 :                                 cur->owner.max_version = winsdb_get_maxVersion(service->wins_db);
     293             :                         }
     294        4943 :                         return cur;
     295             :                 }
     296             :         }
     297             : 
     298          70 :         return NULL;
     299             : }
     300             : 
     301             : /*
     302             :  update the wins_owner_table max_version, if the given version is the highest version
     303             :  if no entry for the wins_owner exists yet, create one
     304             : */
     305        1476 : NTSTATUS wreplsrv_add_table(struct wreplsrv_service *service,
     306             :                             TALLOC_CTX *mem_ctx, struct wreplsrv_owner **_table,
     307             :                             const char *wins_owner, uint64_t version)
     308             : {
     309        1476 :         struct wreplsrv_owner *table = *_table;
     310             :         struct wreplsrv_owner *cur;
     311             : 
     312        1476 :         if (!wins_owner || strcmp(wins_owner, "0.0.0.0") == 0) {
     313           0 :                 wins_owner = service->wins_db->local_owner;
     314             :         }
     315             : 
     316        1476 :         cur = wreplsrv_find_owner(service, table, wins_owner);
     317             : 
     318             :         /* if it doesn't exists yet, create one */
     319        1476 :         if (!cur) {
     320          69 :                 cur = talloc_zero(mem_ctx, struct wreplsrv_owner);
     321          69 :                 NT_STATUS_HAVE_NO_MEMORY(cur);
     322             : 
     323          69 :                 cur->owner.address   = talloc_strdup(cur, wins_owner);
     324          69 :                 NT_STATUS_HAVE_NO_MEMORY(cur->owner.address);
     325          69 :                 cur->owner.min_version       = 0;
     326          69 :                 cur->owner.max_version       = 0;
     327          69 :                 cur->owner.type              = 1; /* don't know why this is always 1 */
     328             : 
     329          69 :                 cur->partner         = wreplsrv_find_partner(service, wins_owner);
     330             : 
     331          69 :                 DLIST_ADD_END(table, cur);
     332          69 :                 *_table = table;
     333             :         }
     334             : 
     335             :         /* the min_version is always 0 here, and won't be updated */
     336             : 
     337             :         /* if the given version is higher than the current max_version, update */
     338        1476 :         if (cur->owner.max_version < version) {
     339        1350 :                 cur->owner.max_version = version;
     340             :                 /* if it's for our local db, we need to update the wins.ldb too */
     341        1350 :                 if (cur == service->owner) {
     342             :                         uint64_t ret;
     343           0 :                         ret = winsdb_set_maxVersion(service->wins_db, cur->owner.max_version);
     344           0 :                         if (ret != cur->owner.max_version) {
     345           0 :                                 DEBUG(0,("winsdb_set_maxVersion(%llu) failed: %llu\n",
     346             :                                          (unsigned long long)cur->owner.max_version, 
     347             :                                          (unsigned long long)ret));
     348           0 :                                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
     349             :                         }
     350             :                 }
     351             :         }
     352             : 
     353        1476 :         return NT_STATUS_OK;
     354             : }
     355             : 
     356             : /*
     357             :   load the partner table
     358             : */
     359          63 : static NTSTATUS wreplsrv_load_table(struct wreplsrv_service *service)
     360             : {
     361          63 :         struct ldb_result *res = NULL;
     362             :         int ret;
     363             :         NTSTATUS status;
     364          63 :         TALLOC_CTX *tmp_ctx = talloc_new(service);
     365          63 :         struct ldb_context *ldb = service->wins_db->ldb;
     366             :         unsigned int i;
     367             :         struct wreplsrv_owner *local_owner;
     368             :         const char *wins_owner;
     369             :         uint64_t version;
     370          63 :         const char * const attrs[] = {
     371             :                 "winsOwner",
     372             :                 "versionID",
     373             :                 NULL
     374             :         };
     375             : 
     376             :         /*
     377             :          * make sure we have our local entry in the list,
     378             :          * but we set service->owner when we're done
     379             :          * to avoid to many calls to wreplsrv_local_max_version()
     380             :          */
     381          63 :         status = wreplsrv_add_table(service,
     382             :                                     service, &service->table,
     383          61 :                                     service->wins_db->local_owner, 0);
     384          63 :         if (!NT_STATUS_IS_OK(status)) goto failed;
     385          63 :         local_owner = wreplsrv_find_owner(service, service->table, service->wins_db->local_owner);
     386          63 :         if (!local_owner) {
     387           0 :                 status = NT_STATUS_INTERNAL_ERROR;
     388           0 :                 goto failed;
     389             :         }
     390             : 
     391             :         /* find the record in the WINS database */
     392          63 :         ret = ldb_search(ldb, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE,
     393             :                          attrs, "(objectClass=winsRecord)");
     394          63 :         status = NT_STATUS_INTERNAL_DB_CORRUPTION;
     395          63 :         if (ret != LDB_SUCCESS) goto failed;
     396             : 
     397          61 :         for (i=0; i < res->count; i++) {
     398           0 :                 wins_owner     = ldb_msg_find_attr_as_string(res->msgs[i], "winsOwner", NULL);
     399           0 :                 version        = ldb_msg_find_attr_as_uint64(res->msgs[i], "versionID", 0);
     400             : 
     401           0 :                 status = wreplsrv_add_table(service,
     402             :                                             service, &service->table,
     403             :                                             wins_owner, version);
     404           0 :                 if (!NT_STATUS_IS_OK(status)) goto failed;
     405           0 :                 talloc_free(res->msgs[i]);
     406             :         }
     407             : 
     408             :         /*
     409             :          * this makes sure we call wreplsrv_local_max_version() before returning in
     410             :          * wreplsrv_find_owner()
     411             :          */
     412          63 :         service->owner = local_owner;
     413             : 
     414             :         /*
     415             :          * this makes sure the maxVersion in the database is updated,
     416             :          * with the highest version we found, if this is higher than the current stored one
     417             :          */
     418         107 :         status = wreplsrv_add_table(service,
     419             :                                     service, &service->table,
     420          63 :                                     service->wins_db->local_owner, local_owner->owner.max_version);
     421          63 :         if (!NT_STATUS_IS_OK(status)) goto failed;
     422             : 
     423          63 :         talloc_free(tmp_ctx);
     424          63 :         return NT_STATUS_OK;
     425           0 : failed:
     426           0 :         talloc_free(tmp_ctx);
     427           0 :         return status;
     428             : }
     429             : 
     430             : /*
     431             :   setup our replication partners
     432             : */
     433          63 : static NTSTATUS wreplsrv_setup_partners(struct wreplsrv_service *service)
     434             : {
     435             :         NTSTATUS status;
     436             : 
     437          63 :         status = wreplsrv_load_partners(service);
     438          63 :         NT_STATUS_NOT_OK_RETURN(status);
     439             : 
     440          63 :         status = wreplsrv_load_table(service);
     441          63 :         NT_STATUS_NOT_OK_RETURN(status);
     442             : 
     443          63 :         return NT_STATUS_OK;
     444             : }
     445             : 
     446             : /*
     447             :   startup the wrepl task
     448             : */
     449          63 : static NTSTATUS wreplsrv_task_init(struct task_server *task)
     450             : {
     451             :         NTSTATUS status;
     452             :         struct wreplsrv_service *service;
     453             : 
     454          63 :         if (!lpcfg_we_are_a_wins_server(task->lp_ctx)) {
     455           0 :                 return NT_STATUS_INVALID_DOMAIN_ROLE;
     456             :         }
     457             : 
     458          63 :         task_server_set_title(task, "task[wreplsrv]");
     459             : 
     460          63 :         service = talloc_zero(task, struct wreplsrv_service);
     461          63 :         if (!service) {
     462           0 :                 task_server_terminate(task, "wreplsrv_task_init: out of memory", true);
     463           0 :                 return NT_STATUS_NO_MEMORY;
     464             :         }
     465          63 :         service->task                = task;
     466          63 :         service->startup_time        = timeval_current();
     467          63 :         task->private_data   = service;
     468             : 
     469             :         /*
     470             :          * setup up all partners, and open the winsdb
     471             :          */
     472          63 :         status = wreplsrv_open_winsdb(service, task->lp_ctx);
     473          63 :         if (!NT_STATUS_IS_OK(status)) {
     474           0 :                 task_server_terminate(task, "wreplsrv_task_init: wreplsrv_open_winsdb() failed", true);
     475           0 :                 return status;
     476             :         }
     477             : 
     478             :         /*
     479             :          * setup timed events for each partner we want to pull from
     480             :          */
     481          63 :         status = wreplsrv_setup_partners(service);
     482          63 :         if (!NT_STATUS_IS_OK(status)) {
     483           0 :                 task_server_terminate(task, "wreplsrv_task_init: wreplsrv_setup_partners() failed", true);
     484           0 :                 return status;
     485             :         }
     486             : 
     487             :         /* 
     488             :          * setup listen sockets, so we can answer requests from our partners,
     489             :          * which pull from us
     490             :          */
     491          63 :         status = wreplsrv_setup_sockets(service, task->lp_ctx);
     492          63 :         if (!NT_STATUS_IS_OK(status)) {
     493           0 :                 task_server_terminate(task, "wreplsrv_task_init: wreplsrv_setup_sockets() failed", true);
     494           0 :                 return status;
     495             :         }
     496             : 
     497          63 :         status = wreplsrv_setup_periodic(service);
     498          63 :         if (!NT_STATUS_IS_OK(status)) {
     499           0 :                 task_server_terminate(task, "wreplsrv_task_init: wreplsrv_setup_periodic() failed", true);
     500           0 :                 return status;
     501             :         }
     502             : 
     503          63 :         irpc_add_name(task->msg_ctx, "wrepl_server");
     504             : 
     505          63 :         return NT_STATUS_OK;
     506             : }
     507             : 
     508             : /*
     509             :   register ourselves as a available server
     510             : */
     511          64 : NTSTATUS server_service_wrepl_init(TALLOC_CTX *ctx)
     512             : {
     513             :         static const struct service_details details = {
     514             :                 .inhibit_fork_on_accept = true,
     515             :                 .inhibit_pre_fork = true,
     516             :                 .task_init = wreplsrv_task_init,
     517             :                 .post_fork = NULL
     518             :         };
     519          64 :         return register_server_service(ctx, "wrepl", &details);
     520             : }

Generated by: LCOV version 1.13