s3-includes: only include system/filesys.h when needed.
[ira/wip.git] / source3 / printing / printer_list.c
1 /*
2    Unix SMB/CIFS implementation.
3    Share Database of available printers.
4    Copyright (C) Simo Sorce 2010
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21 #include "system/filesys.h"
22 #include "dbwrap.h"
23 #include "printer_list.h"
24
25 #define PL_DB_NAME() lock_path("printer_list.tdb")
26 #define PL_KEY_PREFIX "PRINTERLIST/PRN/"
27 #define PL_KEY_FORMAT PL_KEY_PREFIX"%s"
28 #define PL_TIMESTAMP_KEY "PRINTERLIST/GLOBAL/LAST_REFRESH"
29 #define PL_DATA_FORMAT "ddPP"
30 #define PL_TSTAMP_FORMAT "dd"
31
32 static struct db_context *get_printer_list_db(void)
33 {
34         static struct db_context *db;
35
36         if (db != NULL) {
37                 return db;
38         }
39         db = db_open(NULL, PL_DB_NAME(), 0,
40                      TDB_DEFAULT|TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
41                      O_RDWR|O_CREAT, 0644);
42         return db;
43 }
44
45 bool printer_list_parent_init(void)
46 {
47         struct db_context *db;
48
49         /*
50          * Open the tdb in the parent process (smbd) so that our
51          * CLEAR_IF_FIRST optimization in tdb_reopen_all can properly
52          * work.
53          */
54
55         db = get_printer_list_db();
56         if (db == NULL) {
57                 DEBUG(1, ("could not open Printer List Database: %s\n",
58                           strerror(errno)));
59                 return false;
60         }
61         return true;
62 }
63
64 NTSTATUS printer_list_get_printer(TALLOC_CTX *mem_ctx,
65                                   const char *name,
66                                   const char **comment,
67                                   time_t *last_refresh)
68 {
69         struct db_context *db;
70         char *key;
71         TDB_DATA data;
72         uint32_t time_h, time_l;
73         char *nstr = NULL;
74         char *cstr = NULL;
75         NTSTATUS status;
76         int ret;
77
78         db = get_printer_list_db();
79         if (db == NULL) {
80                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
81         }
82
83         key = talloc_asprintf(mem_ctx, PL_KEY_FORMAT, name);
84         if (!key) {
85                 DEBUG(0, ("Failed to allocate key name!\n"));
86                 return NT_STATUS_NO_MEMORY;
87         }
88
89         data = dbwrap_fetch_bystring_upper(db, key, key);
90         if (data.dptr == NULL) {
91                 DEBUG(1, ("Failed to fetch record!\n"));
92                 status = NT_STATUS_NOT_FOUND;
93                 goto done;
94         }
95
96         ret = tdb_unpack(data.dptr, data.dsize,
97                          PL_DATA_FORMAT,
98                          &time_h, &time_l, &nstr, &cstr);
99         if (ret == -1) {
100                 DEBUG(1, ("Failed to un pack printer data"));
101                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
102                 goto done;
103         }
104
105         if (last_refresh) {
106                 *last_refresh = (time_t)(((uint64_t)time_h << 32) + time_l);
107         }
108
109         if (comment) {
110                 *comment = talloc_strdup(mem_ctx, cstr);
111                 if (!*comment) {
112                         DEBUG(1, ("Failed to strdup comment!\n"));
113                         status = NT_STATUS_NO_MEMORY;
114                         goto done;
115                 }
116         }
117
118         status = NT_STATUS_OK;
119
120 done:
121         SAFE_FREE(nstr);
122         SAFE_FREE(cstr);
123         TALLOC_FREE(key);
124         return status;
125 }
126
127 NTSTATUS printer_list_set_printer(TALLOC_CTX *mem_ctx,
128                                   const char *name,
129                                   const char *comment,
130                                   time_t last_refresh)
131 {
132         struct db_context *db;
133         char *key;
134         TDB_DATA data;
135         uint64_t time_64;
136         uint32_t time_h, time_l;
137         const char *str = NULL;
138         NTSTATUS status;
139         int len;
140
141         db = get_printer_list_db();
142         if (db == NULL) {
143                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
144         }
145
146         key = talloc_asprintf(mem_ctx, PL_KEY_FORMAT, name);
147         if (!key) {
148                 DEBUG(0, ("Failed to allocate key name!\n"));
149                 return NT_STATUS_NO_MEMORY;
150         }
151
152         if (comment) {
153                 str = comment;
154         } else {
155                 str = "";
156         }
157
158         time_64 = last_refresh;
159         time_l = time_64 & 0xFFFFFFFFL;
160         time_h = time_64 >> 32;
161
162         len = tdb_pack(NULL, 0, PL_DATA_FORMAT, time_h, time_l, name, str);
163
164         data.dptr = talloc_array(key, uint8_t, len);
165         if (!data.dptr) {
166                 DEBUG(0, ("Failed to allocate tdb data buffer!\n"));
167                 status = NT_STATUS_NO_MEMORY;
168                 goto done;
169         }
170         data.dsize = len;
171
172         len = tdb_pack(data.dptr, data.dsize,
173                        PL_DATA_FORMAT, time_h, time_l, name, str);
174
175         status = dbwrap_store_bystring_upper(db, key, data, TDB_REPLACE);
176
177 done:
178         TALLOC_FREE(key);
179         return status;
180 }
181
182 NTSTATUS printer_list_get_last_refresh(time_t *last_refresh)
183 {
184         struct db_context *db;
185         TDB_DATA data;
186         uint32_t time_h, time_l;
187         NTSTATUS status;
188         int ret;
189
190         db = get_printer_list_db();
191         if (db == NULL) {
192                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
193         }
194
195         ZERO_STRUCT(data);
196
197         data = dbwrap_fetch_bystring(db, talloc_tos(), PL_TIMESTAMP_KEY);
198         if (data.dptr == NULL) {
199                 DEBUG(1, ("Failed to fetch record!\n"));
200                 status = NT_STATUS_NOT_FOUND;
201                 goto done;
202         }
203
204         ret = tdb_unpack(data.dptr, data.dsize,
205                          PL_TSTAMP_FORMAT, &time_h, &time_l);
206         if (ret == -1) {
207                 DEBUG(1, ("Failed to un pack printer data"));
208                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
209                 goto done;
210         }
211
212         *last_refresh = (time_t)(((uint64_t)time_h << 32) + time_l);
213         status = NT_STATUS_OK;
214
215 done:
216         return status;
217 }
218
219 NTSTATUS printer_list_mark_reload(void)
220 {
221         struct db_context *db;
222         TDB_DATA data;
223         uint32_t time_h, time_l;
224         time_t now = time_mono(NULL);
225         NTSTATUS status;
226         int len;
227
228         db = get_printer_list_db();
229         if (db == NULL) {
230                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
231         }
232
233         time_l = ((uint64_t)now) & 0xFFFFFFFFL;
234         time_h = ((uint64_t)now) >> 32;
235
236         len = tdb_pack(NULL, 0, PL_TSTAMP_FORMAT, time_h, time_l);
237
238         data.dptr = talloc_array(talloc_tos(), uint8_t, len);
239         if (!data.dptr) {
240                 DEBUG(0, ("Failed to allocate tdb data buffer!\n"));
241                 status = NT_STATUS_NO_MEMORY;
242                 goto done;
243         }
244         data.dsize = len;
245
246         len = tdb_pack(data.dptr, data.dsize,
247                        PL_TSTAMP_FORMAT, time_h, time_l);
248
249         status = dbwrap_store_bystring(db, PL_TIMESTAMP_KEY,
250                                                 data, TDB_REPLACE);
251
252 done:
253         TALLOC_FREE(data.dptr);
254         return status;
255 }
256
257 typedef int (printer_list_trv_fn_t)(struct db_record *, void *);
258
259 static NTSTATUS printer_list_traverse(printer_list_trv_fn_t *fn,
260                                                 void *private_data)
261 {
262         struct db_context *db;
263         int ret;
264
265         db = get_printer_list_db();
266         if (db == NULL) {
267                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
268         }
269
270         ret = db->traverse(db, fn, private_data);
271         if (ret < 0) {
272                 return NT_STATUS_UNSUCCESSFUL;
273         }
274
275         return NT_STATUS_OK;
276 }
277
278 struct printer_list_clean_state {
279         time_t last_refresh;
280         NTSTATUS status;
281 };
282
283 static int printer_list_clean_fn(struct db_record *rec, void *private_data)
284 {
285         struct printer_list_clean_state *state =
286                         (struct printer_list_clean_state *)private_data;
287         uint32_t time_h, time_l;
288         time_t refresh;
289         char *name;
290         char *comment;
291         int ret;
292
293         /* skip anything that does not contain PL_DATA_FORMAT data */
294         if (strncmp((char *)rec->key.dptr,
295                     PL_KEY_PREFIX, sizeof(PL_KEY_PREFIX)-1)) {
296                 return 0;
297         }
298
299         ret = tdb_unpack(rec->value.dptr, rec->value.dsize,
300                          PL_DATA_FORMAT, &time_h, &time_l, &name, &comment);
301         if (ret == -1) {
302                 DEBUG(1, ("Failed to un pack printer data"));
303                 state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
304                 return -1;
305         }
306
307         SAFE_FREE(name);
308         SAFE_FREE(comment);
309
310         refresh = (time_t)(((uint64_t)time_h << 32) + time_l);
311
312         if (refresh < state->last_refresh) {
313                 state->status = rec->delete_rec(rec);
314                 if (!NT_STATUS_IS_OK(state->status)) {
315                         return -1;
316                 }
317         }
318
319         return 0;
320 }
321
322 NTSTATUS printer_list_clean_old(void)
323 {
324         struct printer_list_clean_state state;
325         NTSTATUS status;
326
327         status = printer_list_get_last_refresh(&state.last_refresh);
328         if (!NT_STATUS_IS_OK(status)) {
329                 return status;
330         }
331
332         state.status = NT_STATUS_OK;
333
334         status = printer_list_traverse(printer_list_clean_fn, &state);
335         if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL) &&
336             !NT_STATUS_IS_OK(state.status)) {
337                 status = state.status;
338         }
339
340         return status;
341 }
342
343 struct printer_list_exec_state {
344         void (*fn)(const char *, const char *, void *);
345         void *private_data;
346         NTSTATUS status;
347 };
348
349 static int printer_list_exec_fn(struct db_record *rec, void *private_data)
350 {
351         struct printer_list_exec_state *state =
352                         (struct printer_list_exec_state *)private_data;
353         uint32_t time_h, time_l;
354         char *name;
355         char *comment;
356         int ret;
357
358         /* always skip PL_TIMESTAMP_KEY key */
359         if (strequal((const char *)rec->key.dptr, PL_TIMESTAMP_KEY)) {
360                 return 0;
361         }
362
363         ret = tdb_unpack(rec->value.dptr, rec->value.dsize,
364                          PL_DATA_FORMAT, &time_h, &time_l, &name, &comment);
365         if (ret == -1) {
366                 DEBUG(1, ("Failed to un pack printer data"));
367                 state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
368                 return -1;
369         }
370
371         state->fn(name, comment, state->private_data);
372
373         SAFE_FREE(name);
374         SAFE_FREE(comment);
375         return 0;
376 }
377
378 NTSTATUS printer_list_run_fn(void (*fn)(const char *, const char *, void *),
379                              void *private_data)
380 {
381         struct printer_list_exec_state state;
382         NTSTATUS status;
383
384         state.fn = fn;
385         state.private_data = private_data;
386         state.status = NT_STATUS_OK;
387
388         status = printer_list_traverse(printer_list_exec_fn, &state);
389         if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL) &&
390             !NT_STATUS_IS_OK(state.status)) {
391                 status = state.status;
392         }
393
394         return status;
395 }