/*************************************************
*     Exim - an Internet mail transport agent    *
*************************************************/

/* Copyright (c) The Exim Maintainers 2021 - 2025 */
/* Copyright (c) University of Cambridge 1995 - 2015 */
/* See the file NOTICE for conditions of use and distribution. */
/* SPDX-License-Identifier: GPL-2.0-or-later */

/* Functions concerned with serialization. */


#include "exim.h"




/*************************************************
*       Test for host or ETRN serialization      *
*************************************************/

/* This function is called when a host is listed for serialization of
connections. It is also called when ETRN is listed for serialization. We open
the misc database and look for a record, which implies an existing connection
or ETRN run. If increasing the count would take us past the given limit
value return FALSE.  If not, bump it and return the new count.  If not found, create
one with value 1 and return TRUE.

Arguments:
  key            string on which to serialize
  lim            parallelism limit

Returns:         >0 if OK to proceed; 0 otherwise
*/


unsigned
enq_start(uschar * key, unsigned lim)
{
const dbdata_serialize * serial_record;
dbdata_serialize new_record;
open_db dbblock;
open_db * dbm_file;

DEBUG(transport) debug_printf("check serialized: %s\n", key);

/* Open and lock the waiting information database. */

if (!(dbm_file = dbfn_open(US"misc", O_RDWR|O_CREAT, &dbblock, TRUE, TRUE)))
  return FALSE;

/* See if there is a record for this host or queue run; if there is, we cannot
proceed with the connection unless the record is very old. */

serial_record = dbfn_read_enforce_length(dbm_file, key, sizeof(dbdata_serialize));
if (serial_record && time(NULL) - serial_record->gen.time_stamp < 6*60*60)
  {
  if (serial_record->count >= lim)
    {
    dbfn_close(dbm_file);
    DEBUG(transport) debug_printf("outstanding serialization record for %s\n",
      key);
    return 0;
    }
  new_record.count = serial_record->count + 1;
  }
else
  new_record.count = 1;

/* We can proceed - insert a new record or update the old one. */

DEBUG(transport) debug_printf("write serialization record for %s val %d\n",
      key, new_record.count);
dbfn_write(dbm_file, key, &new_record, (int)sizeof(dbdata_serialize));
dbfn_close(dbm_file);
return new_record.count;
}



/*************************************************
*              Release serialization             *
*************************************************/

/* This function is called when a serialized host's connection or serialized
ETRN queue run ends. We open the relevant database and delete its record.

Arguments:
  key          the serialization key

Returns:       nothing
*/

void
enq_end(uschar *key)
{
open_db dbblock;
open_db *dbm_file;
dbdata_serialize *serial_record;

DEBUG(transport) debug_printf("end serialized: %s\n", key);

if (  !(dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE, TRUE))
   || !(serial_record = dbfn_read_enforce_length(dbm_file, key, sizeof(dbdata_serialize)))
   )
  return;
if (--serial_record->count > 0)
  {
  DEBUG(transport) debug_printf("write serialization record for %s val %d\n",
      key, serial_record->count);
  dbfn_write(dbm_file, key, serial_record, (int)sizeof(dbdata_serialize));
  }
else
  {
  DEBUG(transport) debug_printf("remove serialization record for %s\n", key);
  dbfn_delete(dbm_file, key);
  }
dbfn_close(dbm_file);
}

/* End of enq.c */
