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