/*++
/* NAME
/*	master_conf 3
/* SUMMARY
/*	Postfix master - master.cf file processing
/* SYNOPSIS
/*	#include "master.h"
/*
/*	void	master_config(serv)
/*	MASTER_SERV *serv;
/*
/*	void	master_refresh(serv)
/*	MASTER_SERV *serv;
/* DESCRIPTION
/*	Use master_config() to read the master.cf configuration file
/*	during program initialization.
/*
/*	Use master_refresh() to re-read the master.cf configuration file
/*	when the process is already running.
/* DIAGNOSTICS
/* BUGS
/* SEE ALSO
/*	master_ent(3), configuration file programmatic interface.
/* LICENSE
/* .ad
/* .fi
/*	The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/*	Wietse Venema
/*	IBM T.J. Watson Research
/*	P.O. Box 704
/*	Yorktown Heights, NY 10598, USA
/*
/*	Wietse Venema
/*	Google, Inc.
/*	111 8th Avenue
/*	New York, NY 10011, USA
/*--*/

/* System libraries. */

#include <sys_defs.h>
#include <unistd.h>
#include <string.h>

/* Utility library. */

#include <msg.h>
#include <argv.h>

/* Application-specific. */

#include "master.h"

/* master_refresh - re-read configuration table */

void    master_refresh(void)
{
    MASTER_SERV *serv;
    MASTER_SERV **servp;

    /*
     * Mark all existing services.
     */
    for (serv = master_head; serv != 0; serv = serv->next)
	serv->flags |= MASTER_FLAG_MARK;

    /*
     * Read the master.cf configuration file. The master_conf() routine
     * unmarks services upon update. New services are born with the mark bit
     * off. After this, anything with the mark bit on should be removed.
     */
    master_config();

    /*
     * Delete all services that are still marked - they disappeared from the
     * configuration file and are therefore no longer needed.
     */
    for (servp = &master_head; (serv = *servp) != 0; /* void */ ) {
	if ((serv->flags & MASTER_FLAG_MARK) != 0) {
	    *servp = serv->next;
	    master_stop_service(serv);
	    free_master_ent(serv);
	} else {
	    servp = &serv->next;
	}
    }
}

/* master_config - read config file */

void    master_config(void)
{
    MASTER_SERV *entry;
    MASTER_SERV *serv;

#define STR_DIFF	strcmp
#define STR_SAME	!strcmp
#define SWAP(type,a,b)	{ type temp = a; a = b; b = temp; }

    /*
     * A service is identified by its endpoint name AND by its transport
     * type, not just by its name alone. The name is unique within its
     * transport type. XXX Service privacy is encoded in the service name.
     */
    set_master_ent();
    while ((entry = get_master_ent()) != 0) {
	if (msg_verbose)
	    print_master_ent(entry);
	for (serv = master_head; serv != 0; serv = serv->next)
	    if (STR_SAME(serv->name, entry->name) && serv->type == entry->type)
		break;

	/*
	 * Add a new service entry. We do not really care in what order the
	 * service entries are kept in memory.
	 */
	if (serv == 0) {
	    entry->next = master_head;
	    master_head = entry;
	    master_start_service(entry);
	}

	/*
	 * Update an existing service entry. Make the current generation of
	 * child processes commit suicide whenever it is convenient. The next
	 * generation of child processes will run with the new configuration
	 * settings.
	 */
	else {
	    if ((serv->flags & MASTER_FLAG_MARK) == 0)
		msg_warn("duplicate master.cf entry for service \"%s\" (%s) "
		     "-- using the last entry", serv->ext_name, serv->name);
	    else
		serv->flags &= ~MASTER_FLAG_MARK;
	    if (entry->flags & MASTER_FLAG_CONDWAKE)
		serv->flags |= MASTER_FLAG_CONDWAKE;
	    else
		serv->flags &= ~MASTER_FLAG_CONDWAKE;
	    serv->wakeup_time = entry->wakeup_time;
	    serv->max_proc = entry->max_proc;
	    serv->throttle_delay = entry->throttle_delay;
	    SWAP(char *, serv->ext_name, entry->ext_name);
	    SWAP(char *, serv->path, entry->path);
	    SWAP(ARGV *, serv->args, entry->args);
	    SWAP(char *, serv->stress_param_val, entry->stress_param_val);
	    master_restart_service(serv, DO_CONF_RELOAD);
	    free_master_ent(entry);
	}
    }
    end_master_ent();
}
