1/*
2   Unix SMB/Netbios implementation.
3   Version 3.0
4   printing backend routines
5   Copyright (C) Andrew Tridgell 1992-2000
6   Copyright (C) Jeremy Allison 2002
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 2 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, write to the Free Software
20   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21*/
22
23#include "includes.h"
24#include "printing.h"
25
26static struct tdb_print_db *print_db_head;
27
28/****************************************************************************
29  Function to find or create the printer specific job tdb given a printername.
30  Limits the number of tdb's open to MAX_PRINT_DBS_OPEN.
31****************************************************************************/
32
33struct tdb_print_db *get_print_db_byname(const char *printername)
34{
35	struct tdb_print_db *p = NULL, *last_entry = NULL;
36	int num_open = 0;
37	pstring printdb_path;
38	BOOL done_become_root = False;
39
40	SMB_ASSERT(printername != NULL);
41
42	for (p = print_db_head, last_entry = print_db_head; p; p = p->next) {
43		/* Ensure the list terminates... JRA. */
44		SMB_ASSERT(p->next != print_db_head);
45
46		if (p->tdb && strequal(p->printer_name, printername)) {
47			DLIST_PROMOTE(print_db_head, p);
48			p->ref_count++;
49			return p;
50		}
51		num_open++;
52		last_entry = p;
53	}
54
55	/* Not found. */
56	if (num_open >= MAX_PRINT_DBS_OPEN) {
57		/* Try and recycle the last entry. */
58		DLIST_PROMOTE(print_db_head, last_entry);
59
60		for (p = print_db_head; p; p = p->next) {
61			if (p->ref_count)
62				continue;
63			if (p->tdb) {
64				if (tdb_close(print_db_head->tdb)) {
65					DEBUG(0,("get_print_db: Failed to close tdb for printer %s\n",
66								print_db_head->printer_name ));
67					return NULL;
68				}
69			}
70			p->tdb = NULL;
71			p->ref_count = 0;
72			memset(p->printer_name, '\0', sizeof(p->printer_name));
73			break;
74		}
75		if (p) {
76			DLIST_PROMOTE(print_db_head, p);
77			p = print_db_head;
78		}
79	}
80
81	if (!p)	{
82		/* Create one. */
83		p = SMB_MALLOC_P(struct tdb_print_db);
84		if (!p) {
85			DEBUG(0,("get_print_db: malloc fail !\n"));
86			return NULL;
87		}
88		ZERO_STRUCTP(p);
89		DLIST_ADD(print_db_head, p);
90	}
91
92	pstrcpy(printdb_path, lock_path("printing/"));
93	pstrcat(printdb_path, printername);
94	pstrcat(printdb_path, ".tdb");
95
96	if (geteuid() != 0) {
97		become_root();
98		done_become_root = True;
99	}
100
101	p->tdb = tdb_open_log(printdb_path, 5000, TDB_DEFAULT, O_RDWR|O_CREAT,
102		0600);
103
104	if (done_become_root)
105		unbecome_root();
106
107	if (!p->tdb) {
108		DEBUG(0,("get_print_db: Failed to open printer backend database %s.\n",
109					printdb_path ));
110		DLIST_REMOVE(print_db_head, p);
111		SAFE_FREE(p);
112		return NULL;
113	}
114	fstrcpy(p->printer_name, printername);
115	p->ref_count++;
116	return p;
117}
118
119/***************************************************************************
120 Remove a reference count.
121****************************************************************************/
122
123void release_print_db( struct tdb_print_db *pdb)
124{
125	pdb->ref_count--;
126	SMB_ASSERT(pdb->ref_count >= 0);
127}
128
129/***************************************************************************
130 Close all open print db entries.
131****************************************************************************/
132
133void close_all_print_db(void)
134{
135	struct tdb_print_db *p = NULL, *next_p = NULL;
136
137	for (p = print_db_head; p; p = next_p) {
138		next_p = p->next;
139
140		if (p->tdb)
141			tdb_close(p->tdb);
142		DLIST_REMOVE(print_db_head, p);
143		ZERO_STRUCTP(p);
144		SAFE_FREE(p);
145	}
146}
147
148
149/****************************************************************************
150 Fetch and clean the pid_t record list for all pids interested in notify
151 messages. data needs freeing on exit.
152****************************************************************************/
153
154TDB_DATA get_printer_notify_pid_list(TDB_CONTEXT *tdb, const char *printer_name, BOOL cleanlist)
155{
156	TDB_DATA data;
157	size_t i;
158
159	ZERO_STRUCT(data);
160
161	data = tdb_fetch_bystring( tdb, NOTIFY_PID_LIST_KEY );
162
163	if (!data.dptr) {
164		ZERO_STRUCT(data);
165		return data;
166	}
167
168	if (data.dsize % 8) {
169		DEBUG(0,("get_printer_notify_pid_list: Size of record for printer %s not a multiple of 8 !\n", printer_name ));
170		tdb_delete_bystring(tdb, NOTIFY_PID_LIST_KEY );
171		SAFE_FREE(data.dptr);
172		ZERO_STRUCT(data);
173		return data;
174	}
175
176	if (!cleanlist)
177		return data;
178
179	/*
180	 * Weed out all dead entries.
181	 */
182
183	for( i = 0; i < data.dsize; i += 8) {
184		pid_t pid = (pid_t)IVAL(data.dptr, i);
185
186		if (pid == sys_getpid())
187			continue;
188
189		/* Entry is dead if process doesn't exist or refcount is zero. */
190
191		while ((i < data.dsize) && ((IVAL(data.dptr, i + 4) == 0) || !process_exists(pid))) {
192
193			/* Refcount == zero is a logic error and should never happen. */
194			if (IVAL(data.dptr, i + 4) == 0) {
195				DEBUG(0,("get_printer_notify_pid_list: Refcount == 0 for pid = %u printer %s !\n",
196							(unsigned int)pid, printer_name ));
197			}
198
199			if (data.dsize - i > 8)
200				memmove( &data.dptr[i], &data.dptr[i+8], data.dsize - i - 8);
201			data.dsize -= 8;
202		}
203	}
204
205	return data;
206}
207
208
209