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