1/*++
2/* NAME
3/*	qmgr_peer 3
4/* SUMMARY
5/*	per-job peers
6/* SYNOPSIS
7/*	#include "qmgr.h"
8/*
9/*	QMGR_PEER *qmgr_peer_create(job, queue)
10/*	QMGR_JOB *job;
11/*	QMGR_QUEUE *queue;
12/*
13/*	QMGR_PEER *qmgr_peer_find(job, queue)
14/*	QMGR_JOB *job;
15/*	QMGR_QUEUE *queue;
16/*
17/*	QMGR_PEER *qmgr_peer_obtain(job, queue)
18/*	QMGR_JOB *job;
19/*	QMGR_QUEUE *queue;
20/*
21/*	void qmgr_peer_free(peer)
22/*	QMGR_PEER *peer;
23/*
24/*	QMGR_PEER *qmgr_peer_select(job)
25/*	QMGR_JOB *job;
26/*
27/* DESCRIPTION
28/*	These routines add/delete/manipulate per-job peers.
29/*	Each peer corresponds to a specific job and destination.
30/*      It is similar to per-transport queue structure, but groups
31/*      only the entries of the given job.
32/*
33/*	qmgr_peer_create() creates an empty peer structure for the named
34/*	job and destination. It is an error to call this function
35/*	if a peer for given combination already exists.
36/*
37/*	qmgr_peer_find() looks up the peer for the named destination
38/*	for the named job. A null result means that the peer
39/*	was not found.
40/*
41/*	qmgr_peer_obtain() looks up the peer for the named destination
42/*	for the named job. If it doesn't exist yet, it creates it.
43/*
44/*	qmgr_peer_free() disposes of a per-job peer after all
45/*	its entries have been taken care of. It is an error to dispose
46/*	of a peer still in use.
47/*
48/*	qmgr_peer_select() attempts to find a peer of named job that
49/*	has messages pending delivery.  This routine implements
50/*	round-robin search among job's peers.
51/* DIAGNOSTICS
52/*	Panic: consistency check failure.
53/* LICENSE
54/* .ad
55/* .fi
56/*	The Secure Mailer license must be distributed with this software.
57/* AUTHOR(S)
58/*	Patrik Rak
59/*	patrik@raxoft.cz
60/*--*/
61
62/* System library. */
63
64#include <sys_defs.h>
65
66/* Utility library. */
67
68#include <msg.h>
69#include <htable.h>
70#include <mymalloc.h>
71
72/* Application-specific. */
73
74#include "qmgr.h"
75
76/* qmgr_peer_create - create and initialize message peer structure */
77
78QMGR_PEER *qmgr_peer_create(QMGR_JOB *job, QMGR_QUEUE *queue)
79{
80    QMGR_PEER *peer;
81
82    peer = (QMGR_PEER *) mymalloc(sizeof(QMGR_PEER));
83    peer->queue = queue;
84    peer->job = job;
85    QMGR_LIST_APPEND(job->peer_list, peer, peers);
86    htable_enter(job->peer_byname, queue->name, (char *) peer);
87    peer->refcount = 0;
88    QMGR_LIST_INIT(peer->entry_list);
89    return (peer);
90}
91
92/* qmgr_peer_free - release peer structure */
93
94void    qmgr_peer_free(QMGR_PEER *peer)
95{
96    const char *myname = "qmgr_peer_free";
97    QMGR_JOB *job = peer->job;
98    QMGR_QUEUE *queue = peer->queue;
99
100    /*
101     * Sanity checks. It is an error to delete a referenced peer structure.
102     */
103    if (peer->refcount != 0)
104	msg_panic("%s: refcount: %d", myname, peer->refcount);
105    if (peer->entry_list.next != 0)
106	msg_panic("%s: entry list not empty: %s", myname, queue->name);
107
108    QMGR_LIST_UNLINK(job->peer_list, QMGR_PEER *, peer, peers);
109    htable_delete(job->peer_byname, queue->name, (void (*) (char *)) 0);
110    myfree((char *) peer);
111}
112
113/* qmgr_peer_find - lookup peer associated with given job and queue */
114
115QMGR_PEER *qmgr_peer_find(QMGR_JOB *job, QMGR_QUEUE *queue)
116{
117    return ((QMGR_PEER *) htable_find(job->peer_byname, queue->name));
118}
119
120/* qmgr_peer_obtain - find/create peer associated with given job and queue */
121
122QMGR_PEER *qmgr_peer_obtain(QMGR_JOB *job, QMGR_QUEUE *queue)
123{
124    QMGR_PEER *peer;
125
126    if ((peer = qmgr_peer_find(job, queue)) == 0)
127	peer = qmgr_peer_create(job, queue);
128    return (peer);
129}
130
131/* qmgr_peer_select - select next peer suitable for delivery within given job */
132
133QMGR_PEER *qmgr_peer_select(QMGR_JOB *job)
134{
135    QMGR_PEER *peer;
136    QMGR_QUEUE *queue;
137
138    /*
139     * If we find a suitable site, rotate the list to enforce round-robin
140     * selection. See similar selection code in qmgr_transport_select().
141     */
142    for (peer = job->peer_list.next; peer; peer = peer->peers.next) {
143	queue = peer->queue;
144	if (queue->window > queue->busy_refcount && peer->entry_list.next != 0) {
145	    QMGR_LIST_ROTATE(job->peer_list, peer, peers);
146	    if (msg_verbose)
147		msg_info("qmgr_peer_select: %s %s %s (%d of %d)",
148		job->message->queue_id, queue->transport->name, queue->name,
149			 queue->busy_refcount + 1, queue->window);
150	    return (peer);
151	}
152    }
153    return (0);
154}
155