dbwrap: add a dbwrap_flags argument to db_open()
[samba.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/dbwrap.h"
23 #include "dbwrap/dbwrap_open.h"
24 #include "util_tdb.h"
25 #include "printer_list.h"
26
27 #define PL_DB_NAME() lock_path("printer_list.tdb")
28 #define PL_KEY_PREFIX "PRINTERLIST/PRN/"
29 #define PL_KEY_FORMAT PL_KEY_PREFIX"%s"
30 #define PL_TIMESTAMP_KEY "PRINTERLIST/GLOBAL/LAST_REFRESH"
31 #define PL_DATA_FORMAT "ddPPP"
32 #define PL_TSTAMP_FORMAT "dd"
33
34 static struct db_context *get_printer_list_db(void)
35 {
36         static struct db_context *db;
37
38         if (db != NULL) {
39                 return db;
40         }
41         db = db_open(NULL, PL_DB_NAME(), 0,
42                      TDB_DEFAULT|TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
43                      O_RDWR|O_CREAT, 0644, DBWRAP_LOCK_ORDER_1,
44                      DBWRAP_FLAG_NONE);
45         return db;
46 }
47
48 bool printer_list_parent_init(void)
49 {
50         struct db_context *db;
51
52         /*
53          * Open the tdb in the parent process (smbd) so that our
54          * CLEAR_IF_FIRST optimization in tdb_reopen_all can properly
55          * work.
56          */
57
58         db = get_printer_list_db();
59         if (db == NULL) {
60                 DEBUG(1, ("could not open Printer List Database: %s\n",
61                           strerror(errno)));
62                 return false;
63         }
64         return true;
65 }
66
67 NTSTATUS printer_list_get_printer(TALLOC_CTX *mem_ctx,
68                                   const char *name,
69                                   const char **comment,
70                                   const char **location,
71                                   time_t *last_refresh)
72 {
73         struct db_context *db;
74         char *key;
75         TDB_DATA data;
76         uint32_t time_h, time_l;
77         char *nstr = NULL;
78         char *cstr = NULL;
79         char *lstr = NULL;
80         NTSTATUS status;
81         int ret;
82
83         db = get_printer_list_db();
84         if (db == NULL) {
85                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
86         }
87
88         key = talloc_asprintf(mem_ctx, PL_KEY_FORMAT, name);
89         if (!key) {
90                 DEBUG(0, ("Failed to allocate key name!\n"));
91                 return NT_STATUS_NO_MEMORY;
92         }
93
94         status = dbwrap_fetch_bystring_upper(db, key, key, &data);
95         if (!NT_STATUS_IS_OK(status)) {
96                 DEBUG(6, ("Failed to fetch record! "
97                           "The printer database is empty?\n"));
98                 goto done;
99         }
100
101         ret = tdb_unpack(data.dptr, data.dsize,
102                          PL_DATA_FORMAT,
103                          &time_h, &time_l, &nstr, &cstr, &lstr);
104         if (ret == -1) {
105                 DEBUG(1, ("Failed to un pack printer data"));
106                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
107                 goto done;
108         }
109
110         if (last_refresh) {
111                 *last_refresh = (time_t)(((uint64_t)time_h << 32) + time_l);
112         }
113
114         if (comment) {
115                 *comment = talloc_strdup(mem_ctx, cstr);
116                 if (!*comment) {
117                         DEBUG(1, ("Failed to strdup comment!\n"));
118                         status = NT_STATUS_NO_MEMORY;
119                         goto done;
120                 }
121         }
122
123         if (location) {
124                 *location = talloc_strdup(mem_ctx, lstr);
125                 if (*location == NULL) {
126                         DEBUG(1, ("Failed to strdup location!\n"));
127                         status = NT_STATUS_NO_MEMORY;
128                         goto done;
129                 }
130         }
131
132         status = NT_STATUS_OK;
133
134 done:
135         SAFE_FREE(nstr);
136         SAFE_FREE(cstr);
137         TALLOC_FREE(key);
138         return status;
139 }
140
141 NTSTATUS printer_list_set_printer(TALLOC_CTX *mem_ctx,
142                                   const char *name,
143                                   const char *comment,
144                                   const char *location,
145                                   time_t last_refresh)
146 {
147         struct db_context *db;
148         char *key;
149         TDB_DATA data;
150         uint64_t time_64;
151         uint32_t time_h, time_l;
152         NTSTATUS status;
153         int len;
154
155         db = get_printer_list_db();
156         if (db == NULL) {
157                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
158         }
159
160         key = talloc_asprintf(mem_ctx, PL_KEY_FORMAT, name);
161         if (!key) {
162                 DEBUG(0, ("Failed to allocate key name!\n"));
163                 return NT_STATUS_NO_MEMORY;
164         }
165
166         if (comment == NULL) {
167                 comment = "";
168         }
169
170         if (location == NULL) {
171                 location = "";
172         }
173
174         time_64 = last_refresh;
175         time_l = time_64 & 0xFFFFFFFFL;
176         time_h = time_64 >> 32;
177
178         len = tdb_pack(NULL, 0,
179                        PL_DATA_FORMAT,
180                        time_h,
181                        time_l,
182                        name,
183                        comment,
184                        location);
185
186         data.dptr = talloc_array(key, uint8_t, len);
187         if (!data.dptr) {
188                 DEBUG(0, ("Failed to allocate tdb data buffer!\n"));
189                 status = NT_STATUS_NO_MEMORY;
190                 goto done;
191         }
192         data.dsize = len;
193
194         len = tdb_pack(data.dptr, data.dsize,
195                        PL_DATA_FORMAT,
196                        time_h,
197                        time_l,
198                        name,
199                        comment,
200                        location);
201
202         status = dbwrap_store_bystring_upper(db, key, data, TDB_REPLACE);
203
204 done:
205         TALLOC_FREE(key);
206         return status;
207 }
208
209 NTSTATUS printer_list_get_last_refresh(time_t *last_refresh)
210 {
211         struct db_context *db;
212         TDB_DATA data;
213         uint32_t time_h, time_l;
214         NTSTATUS status;
215         int ret;
216
217         db = get_printer_list_db();
218         if (db == NULL) {
219                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
220         }
221
222         ZERO_STRUCT(data);
223
224         status = dbwrap_fetch_bystring(db, talloc_tos(), PL_TIMESTAMP_KEY, &data);
225         if (!NT_STATUS_IS_OK(status)) {
226                 DEBUG(1, ("Failed to fetch record!\n"));
227                 goto done;
228         }
229
230         ret = tdb_unpack(data.dptr, data.dsize,
231                          PL_TSTAMP_FORMAT, &time_h, &time_l);
232         if (ret == -1) {
233                 DEBUG(1, ("Failed to un pack printer data"));
234                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
235                 goto done;
236         }
237
238         *last_refresh = (time_t)(((uint64_t)time_h << 32) + time_l);
239         status = NT_STATUS_OK;
240
241 done:
242         return status;
243 }
244
245 NTSTATUS printer_list_mark_reload(void)
246 {
247         struct db_context *db;
248         TDB_DATA data;
249         uint32_t time_h, time_l;
250         time_t now = time_mono(NULL);
251         NTSTATUS status;
252         int len;
253
254         db = get_printer_list_db();
255         if (db == NULL) {
256                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
257         }
258
259         time_l = ((uint64_t)now) & 0xFFFFFFFFL;
260         time_h = ((uint64_t)now) >> 32;
261
262         len = tdb_pack(NULL, 0, PL_TSTAMP_FORMAT, time_h, time_l);
263
264         data.dptr = talloc_array(talloc_tos(), uint8_t, len);
265         if (!data.dptr) {
266                 DEBUG(0, ("Failed to allocate tdb data buffer!\n"));
267                 status = NT_STATUS_NO_MEMORY;
268                 goto done;
269         }
270         data.dsize = len;
271
272         len = tdb_pack(data.dptr, data.dsize,
273                        PL_TSTAMP_FORMAT, time_h, time_l);
274
275         status = dbwrap_store_bystring(db, PL_TIMESTAMP_KEY,
276                                                 data, TDB_REPLACE);
277
278 done:
279         TALLOC_FREE(data.dptr);
280         return status;
281 }
282
283 typedef int (printer_list_trv_fn_t)(struct db_record *, void *);
284
285 static NTSTATUS printer_list_traverse(printer_list_trv_fn_t *fn,
286                                                 void *private_data)
287 {
288         struct db_context *db;
289         NTSTATUS status;
290
291         db = get_printer_list_db();
292         if (db == NULL) {
293                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
294         }
295
296         status = dbwrap_traverse(db, fn, private_data, NULL);
297
298         return status;
299 }
300
301 struct printer_list_clean_state {
302         time_t last_refresh;
303         NTSTATUS status;
304 };
305
306 static int printer_list_clean_fn(struct db_record *rec, void *private_data)
307 {
308         struct printer_list_clean_state *state =
309                         (struct printer_list_clean_state *)private_data;
310         uint32_t time_h, time_l;
311         time_t refresh;
312         char *name;
313         char *comment;
314         char *location;
315         int ret;
316         TDB_DATA key;
317         TDB_DATA value;
318
319         key = dbwrap_record_get_key(rec);
320
321         /* skip anything that does not contain PL_DATA_FORMAT data */
322         if (strncmp((char *)key.dptr,
323                     PL_KEY_PREFIX, sizeof(PL_KEY_PREFIX)-1)) {
324                 return 0;
325         }
326
327         value = dbwrap_record_get_value(rec);
328
329         ret = tdb_unpack(value.dptr, value.dsize,
330                          PL_DATA_FORMAT, &time_h, &time_l, &name, &comment,
331                          &location);
332         if (ret == -1) {
333                 DEBUG(1, ("Failed to un pack printer data"));
334                 state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
335                 return -1;
336         }
337
338         SAFE_FREE(name);
339         SAFE_FREE(comment);
340         SAFE_FREE(location);
341
342         refresh = (time_t)(((uint64_t)time_h << 32) + time_l);
343
344         if (refresh < state->last_refresh) {
345                 state->status = dbwrap_record_delete(rec);
346                 if (!NT_STATUS_IS_OK(state->status)) {
347                         return -1;
348                 }
349         }
350
351         return 0;
352 }
353
354 NTSTATUS printer_list_clean_old(void)
355 {
356         struct printer_list_clean_state state;
357         NTSTATUS status;
358
359         status = printer_list_get_last_refresh(&state.last_refresh);
360         if (!NT_STATUS_IS_OK(status)) {
361                 return status;
362         }
363
364         state.status = NT_STATUS_OK;
365
366         status = printer_list_traverse(printer_list_clean_fn, &state);
367         if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL) &&
368             !NT_STATUS_IS_OK(state.status)) {
369                 status = state.status;
370         }
371
372         return status;
373 }
374
375 struct printer_list_exec_state {
376         void (*fn)(const char *, const char *, const char *, void *);
377         void *private_data;
378         NTSTATUS status;
379 };
380
381 static int printer_list_exec_fn(struct db_record *rec, void *private_data)
382 {
383         struct printer_list_exec_state *state =
384                         (struct printer_list_exec_state *)private_data;
385         uint32_t time_h, time_l;
386         char *name;
387         char *comment;
388         char *location;
389         int ret;
390         TDB_DATA key;
391         TDB_DATA value;
392
393         key = dbwrap_record_get_key(rec);
394
395         /* always skip PL_TIMESTAMP_KEY key */
396         if (strequal((const char *)key.dptr, PL_TIMESTAMP_KEY)) {
397                 return 0;
398         }
399
400         value = dbwrap_record_get_value(rec);
401
402         ret = tdb_unpack(value.dptr, value.dsize,
403                          PL_DATA_FORMAT, &time_h, &time_l, &name, &comment,
404                          &location);
405         if (ret == -1) {
406                 DEBUG(1, ("Failed to un pack printer data"));
407                 state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
408                 return -1;
409         }
410
411         state->fn(name, comment, location, state->private_data);
412
413         SAFE_FREE(name);
414         SAFE_FREE(comment);
415         SAFE_FREE(location);
416         return 0;
417 }
418
419 NTSTATUS printer_list_run_fn(void (*fn)(const char *, const char *, const char *, void *),
420                              void *private_data)
421 {
422         struct printer_list_exec_state state;
423         NTSTATUS status;
424
425         state.fn = fn;
426         state.private_data = private_data;
427         state.status = NT_STATUS_OK;
428
429         status = printer_list_traverse(printer_list_exec_fn, &state);
430         if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL) &&
431             !NT_STATUS_IS_OK(state.status)) {
432                 status = state.status;
433         }
434
435         return status;
436 }