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