• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  * Copyright (C) 2016 Mopria Alliance, Inc.
4  * Copyright (C) 2013 Hewlett-Packard Development Company, L.P.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 #ifndef _GNU_SOURCE
20 #define _GNU_SOURCE
21 #endif
22 
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <semaphore.h>
26 #include <fcntl.h>
27 
28 #include "lib_wprint.h"
29 #include "ippstatus_monitor.h"
30 #include "ipphelper.h"
31 
32 #include "cups.h"
33 #include "http-private.h"
34 #include <pthread.h>
35 #include "wprint_debug.h"
36 
37 #define TAG "ippstatus_monitor"
38 
39 static void _init(const ifc_status_monitor_t *this_p, const wprint_connect_info_t *);
40 
41 static void _get_status(const ifc_status_monitor_t *this_p, printer_state_dyn_t *printer_state_dyn);
42 
43 static void _start(const ifc_status_monitor_t *this_p, void (*status_cb)(
44         const printer_state_dyn_t *new_status, const printer_state_dyn_t *old_status,
45                 void *status_param),
46                 void (*job_state_cb)(const job_state_dyn_t *new_state, void *param), void *param);
47 
48 static void _stop(const ifc_status_monitor_t *this_p);
49 
50 static status_t _cancel(const ifc_status_monitor_t *this_p, const char *requesting_user);
51 
52 static void _destroy(const ifc_status_monitor_t *this_p);
53 
54 static void _get_job_state(const ifc_status_monitor_t *this_p, job_state_dyn_t *job_state_dyn,
55         int job_id);
56 
57 static const ifc_status_monitor_t _status_ifc = {.init = _init, .get_status = _get_status,
58         .cancel = _cancel, .start = _start, .stop = _stop, .destroy = _destroy,};
59 
60 typedef struct {
61     unsigned char initialized;
62     http_t *http;
63     char printer_uri[1024];
64     char http_resource[1024];
65     unsigned char stop_monitor;
66     unsigned char monitor_running;
67     sem_t monitor_sem;
68     pthread_mutex_t mutex;
69     pthread_mutexattr_t mutexattr;
70     ifc_status_monitor_t ifc;
71     char requesting_user[1024];
72 } ipp_monitor_t;
73 
ipp_status_get_monitor_ifc(const ifc_wprint_t * wprint_ifc)74 const ifc_status_monitor_t *ipp_status_get_monitor_ifc(const ifc_wprint_t *wprint_ifc) {
75     ipp_monitor_t *monitor = (ipp_monitor_t *) malloc(sizeof(ipp_monitor_t));
76 
77     // setup the interface
78     monitor->initialized = 0;
79     monitor->http = NULL;
80     memcpy(&monitor->ifc, &_status_ifc, sizeof(ifc_status_monitor_t));
81     return &monitor->ifc;
82 }
83 
_init(const ifc_status_monitor_t * this_p,const wprint_connect_info_t * connect_info)84 static void _init(const ifc_status_monitor_t *this_p, const wprint_connect_info_t *connect_info) {
85     ipp_monitor_t *monitor;
86     LOGD("_init(): enter");
87     do {
88         if (this_p == NULL) {
89             continue;
90         }
91         monitor = IMPL(ipp_monitor_t, ifc, this_p);
92 
93         if (monitor->initialized != 0) {
94             sem_post(&monitor->monitor_sem);
95             sem_destroy(&monitor->monitor_sem);
96 
97             pthread_mutex_unlock(&monitor->mutex);
98             pthread_mutex_destroy(&monitor->mutex);
99         }
100 
101         if (monitor->http != NULL) {
102             httpClose(monitor->http);
103         }
104 
105         monitor->http = ipp_cups_connect(connect_info, monitor->printer_uri,
106                 sizeof(monitor->printer_uri));
107         getResourceFromURI(monitor->printer_uri, monitor->http_resource, 1024);
108 
109         monitor->monitor_running = 0;
110         monitor->stop_monitor = 0;
111 
112         pthread_mutexattr_init(&monitor->mutexattr);
113         pthread_mutexattr_settype(&(monitor->mutexattr), PTHREAD_MUTEX_RECURSIVE_NP);
114         pthread_mutex_init(&monitor->mutex, &monitor->mutexattr);
115         sem_init(&monitor->monitor_sem, 0, 0);
116         monitor->initialized = 1;
117     } while (0);
118 }
119 
_destroy(const ifc_status_monitor_t * this_p)120 static void _destroy(const ifc_status_monitor_t *this_p) {
121     ipp_monitor_t *monitor;
122     LOGD("_destroy(): enter");
123     do {
124         if (this_p == NULL) {
125             continue;
126         }
127 
128         monitor = IMPL(ipp_monitor_t, ifc, this_p);
129         if (monitor->initialized) {
130             pthread_mutex_lock(&monitor->mutex);
131 
132             sem_post(&monitor->monitor_sem);
133             sem_destroy(&monitor->monitor_sem);
134 
135             pthread_mutex_unlock(&monitor->mutex);
136             pthread_mutex_destroy(&monitor->mutex);
137         }
138 
139         if (monitor->http != NULL) {
140             httpClose(monitor->http);
141         }
142 
143         free(monitor);
144     } while (0);
145 }
146 
_get_status(const ifc_status_monitor_t * this_p,printer_state_dyn_t * printer_state_dyn)147 static void _get_status(const ifc_status_monitor_t *this_p,
148         printer_state_dyn_t *printer_state_dyn) {
149     int i;
150     ipp_monitor_t *monitor;
151     ipp_pstate_t printer_state;
152     ipp_status_t ipp_status;
153     LOGD("_get_status(): enter");
154     do {
155         if (printer_state_dyn == NULL) {
156             LOGD("_get_status(): printer_state_dyn is null!");
157             continue;
158         }
159 
160         printer_state_dyn->printer_status = PRINT_STATUS_UNKNOWN;
161         printer_state_dyn->printer_reasons[0] = PRINT_STATUS_UNKNOWN;
162         for (i = 0; i <= PRINT_STATUS_MAX_STATE; i++) {
163             printer_state_dyn->printer_reasons[i] = PRINT_STATUS_MAX_STATE;
164         }
165 
166         if (this_p == NULL) {
167             LOGE("_get_status(): this_p is null!");
168             continue;
169         }
170 
171         monitor = IMPL(ipp_monitor_t, ifc, this_p);
172         if (!monitor->initialized) {
173             LOGE("_get_status(): Monitor is uninitialized");
174             continue;
175         }
176 
177         if (monitor->http == NULL) {
178             LOGE("_get_status(): monitor->http is NULL, setting Unable to Connect");
179             printer_state_dyn->printer_reasons[0] = PRINT_STATUS_UNABLE_TO_CONNECT;
180             continue;
181         }
182 
183         printer_state_dyn->printer_status = PRINT_STATUS_IDLE;
184         ipp_status = get_PrinterState(monitor->http, monitor->printer_uri, printer_state_dyn,
185                 &printer_state);
186         LOGD("_get_status(): ipp_status=%d", ipp_status);
187         debuglist_printerStatus(printer_state_dyn);
188     } while (0);
189 }
190 
_start(const ifc_status_monitor_t * this_p,void (* status_cb)(const printer_state_dyn_t * new_status,const printer_state_dyn_t * old_status,void * status_param),void (* job_state_cb)(const job_state_dyn_t * new_state,void * param),void * param)191 static void _start(const ifc_status_monitor_t *this_p,
192         void (*status_cb)(const printer_state_dyn_t *new_status,
193                 const printer_state_dyn_t *old_status, void *status_param),
194         void (*job_state_cb)(const job_state_dyn_t *new_state, void *param),
195         void *param) {
196     int i, job_id = -1;
197     printer_state_dyn_t last_status, curr_status;
198     job_state_dyn_t old_state, new_state;
199     ipp_monitor_t *monitor = NULL;
200 
201     LOGD("_start(): enter");
202 
203     // initialize our status structures
204     for (i = 0; i <= PRINT_STATUS_MAX_STATE; i++) {
205         curr_status.printer_reasons[i] = PRINT_STATUS_MAX_STATE;
206         last_status.printer_reasons[i] = PRINT_STATUS_MAX_STATE;
207     }
208 
209     last_status.printer_status = PRINT_STATUS_UNKNOWN;
210     last_status.printer_reasons[0] = PRINT_STATUS_INITIALIZING;
211 
212     curr_status.printer_status = PRINT_STATUS_UNKNOWN;
213     curr_status.printer_reasons[0] = PRINT_STATUS_INITIALIZING;
214 
215     // send out the first callback
216     if (status_cb != NULL) {
217         (*status_cb)(&curr_status, &last_status, param);
218     }
219 
220     /* initialize job status structures */
221     for (i = 0; i <= IPP_JOB_STATE_REASON_MAX_VALUE; i++) {
222         new_state.job_state_reasons[i] = IPP_JOB_STATE_REASON_MAX_VALUE;
223         old_state.job_state_reasons[i] = IPP_JOB_STATE_REASON_MAX_VALUE;
224     }
225 
226     old_state.job_state = IPP_JOB_STATE_UNKNOWN;
227     old_state.job_state_reasons[0] = IPP_JOB_STATE_REASON_UNKNOWN;
228 
229     new_state.job_state = IPP_JOB_STATE_UNKNOWN;
230     new_state.job_state_reasons[0] = IPP_JOB_STATE_REASON_UNKNOWN;
231 
232     do {
233         curr_status.printer_status = PRINT_STATUS_SVC_REQUEST;
234         curr_status.printer_reasons[0] = PRINT_STATUS_UNABLE_TO_CONNECT;
235 
236         if (this_p == NULL) {
237             continue;
238         }
239 
240         monitor = IMPL(ipp_monitor_t, ifc, this_p);
241         if (!monitor->initialized) {
242             continue;
243         }
244 
245         if (monitor->monitor_running) {
246             continue;
247         }
248 
249         monitor->stop_monitor = 0;
250         monitor->monitor_running = 1;
251         if (monitor->http == NULL) {
252             if (status_cb != NULL) {
253                 (*status_cb)(&curr_status, &last_status, param);
254             }
255             sem_wait(&monitor->monitor_sem);
256 
257             last_status.printer_status = PRINT_STATUS_UNKNOWN;
258             last_status.printer_reasons[0] = PRINT_STATUS_SHUTTING_DOWN;
259 
260             curr_status.printer_status = PRINT_STATUS_UNKNOWN;
261             curr_status.printer_reasons[0] = PRINT_STATUS_SHUTTING_DOWN;
262         } else {
263             while (!monitor->stop_monitor) {
264                 pthread_mutex_lock(&monitor->mutex);
265                 _get_status(this_p, &curr_status);
266                 pthread_mutex_unlock(&monitor->mutex);
267                 if ((status_cb != NULL) &&
268                         (memcmp(&curr_status, &last_status, sizeof(printer_state_dyn_t)) != 0)) {
269                     (*status_cb)(&curr_status, &last_status, param);
270                     memcpy(&last_status, &curr_status, sizeof(printer_state_dyn_t));
271                 }
272 
273                 // Do not call for job state if thread has been stopped
274                 if (job_state_cb != NULL && !monitor->stop_monitor) {
275                     pthread_mutex_lock(&monitor->mutex);
276                     if (job_id == -1) {
277                         job_id = getJobId(monitor->http, monitor->http_resource,
278                                           monitor->printer_uri, &new_state,
279                                           monitor->requesting_user);
280                     }
281                     _get_job_state(this_p, &new_state, job_id);
282                     pthread_mutex_unlock(&monitor->mutex);
283 
284                     if (memcmp(&new_state, &old_state, sizeof(job_state_dyn_t)) != 0) {
285                         (*job_state_cb)(&new_state, param);
286                         memcpy(&old_state, &new_state, sizeof(job_state_dyn_t));
287                     }
288                 }
289                 sleep(1);
290             }
291         }
292         monitor->monitor_running = 0;
293     } while (0);
294 
295     if (status_cb != NULL) {
296         (*status_cb)(&curr_status, &last_status, param);
297     }
298 }
299 
_stop(const ifc_status_monitor_t * this_p)300 static void _stop(const ifc_status_monitor_t *this_p) {
301     // request a stop and release the semaphore
302     ipp_monitor_t *monitor;
303     LOGD("_stop(): enter");
304     do {
305         if (this_p == NULL) {
306             continue;
307         }
308 
309         monitor = IMPL(ipp_monitor_t, ifc, this_p);
310         if (!monitor->initialized) {
311             continue;
312         }
313 
314         sem_post(&monitor->monitor_sem);
315         monitor->stop_monitor = 1;
316     } while (0);
317 }
318 
_cancel(const ifc_status_monitor_t * this_p,const char * requesting_user)319 static status_t _cancel(const ifc_status_monitor_t *this_p, const char *requesting_user) {
320     status_t return_value = ERROR;
321     int job_id = -1;
322     ipp_monitor_t *monitor = NULL;
323     ipp_t *request = NULL;
324     ipp_t *response = NULL;
325     ipp_attribute_t *attr;
326 
327     LOGD("_cancel(): enter");
328 
329     monitor = IMPL(ipp_monitor_t, ifc, this_p);
330     if (this_p != NULL && monitor != NULL && monitor->initialized) {
331         pthread_mutex_lock(&monitor->mutex);
332         do {
333             if (monitor->stop_monitor) {
334                 break;
335             }
336 
337             request = ippNewRequest(IPP_GET_JOBS);
338             if (request == NULL) {
339                 break;
340             }
341 
342             ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
343                     monitor->printer_uri);
344             ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1);
345             ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
346                     NULL, requesting_user);
347 
348             // Requested printer attributes
349             static const char *pattrs[] = {"job-id", "job-state", "job-state-reasons"};
350 
351             ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes",
352                     sizeof(pattrs) / sizeof(pattrs[1]), NULL, pattrs);
353 
354             response = ipp_doCupsRequest(monitor->http, request, monitor->http_resource,
355                     monitor->printer_uri);
356             if (response == NULL) {
357                 ipp_status_t ipp_status = cupsLastError();
358                 LOGD("_cancel get job attributes: response is null, ipp_status %d: %s",
359                         ipp_status, ippErrorString(ipp_status));
360                 return_value = ERROR;
361             } else {
362                 attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER);
363                 if (attr != NULL) {
364                     job_id = ippGetInteger(attr, 0);
365                     LOGD("_cancel got job-id: %d", job_id);
366                 } else { // We need the job id to attempt a cancel
367                     break;
368                 }
369 
370                 attr = ippFindAttribute(response, "job-state", IPP_TAG_ENUM);
371                 if (attr != NULL) {
372                     ipp_jstate_t jobState = (ipp_jstate_t)ippGetInteger(attr, 0);
373                     LOGD("_cancel got job-state: %d", jobState);
374                 }
375 
376                 attr = ippFindAttribute(response, "job-state-reasons", IPP_TAG_KEYWORD);
377                 if (attr != NULL) {
378                     int idx;
379                     for (idx = 0; idx < ippGetCount(attr); idx++) {
380                         LOGD("before job-state-reason (%d): %s", idx,
381                                 ippGetString(attr, idx, NULL));
382                     }
383                 }
384             }
385         } while (0);
386 
387         ippDelete(request);
388         request = NULL;
389         ippDelete(response);
390         response = NULL;
391 
392         do {
393             if (job_id == -1) {
394                 break;
395             }
396 
397             request = ippNewRequest(IPP_CANCEL_JOB);
398             if (request == NULL) {
399                 break;
400             }
401 
402             ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
403                     monitor->printer_uri);
404             ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", job_id);
405             ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
406                     "requesting-user-name", NULL, requesting_user);
407 
408             if ((response = ipp_doCupsRequest(monitor->http, request, monitor->http_resource,
409                     monitor->printer_uri)) == NULL) {
410                 ipp_status_t ipp_status = cupsLastError();
411                 LOGD("cancel:  response is null:  ipp_status %d %s", ipp_status,
412                         ippErrorString(ipp_status));
413                 return_value = ERROR;
414             } else {
415                 ipp_status_t ipp_status = cupsLastError();
416                 LOGE("IPP_Status for cancel request was %d %s", ipp_status,
417                         ippErrorString(ipp_status));
418                 attr = ippFindAttribute(response, "job-state-reasons", IPP_TAG_KEYWORD);
419                 if (attr != NULL) {
420                     int idx;
421                     for (idx = 0; ippGetCount(attr); idx++) {
422                         LOGD("job-state-reason (%d): %s", idx, ippGetString(attr, idx, NULL));
423                     }
424                 }
425                 return_value = OK;
426             }
427         } while (0);
428 
429         ippDelete(request);
430         ippDelete(response);
431 
432         if (monitor->initialized) {
433             pthread_mutex_unlock(&monitor->mutex);
434         }
435     }
436     return return_value;
437 }
438 
439 /*
440  * Get job state for the given job_id
441  */
_get_job_state(const ifc_status_monitor_t * this_p,job_state_dyn_t * job_state_dyn,int job_id)442 static void _get_job_state(const ifc_status_monitor_t *this_p, job_state_dyn_t *job_state_dyn,
443                            int job_id) {
444     if (job_id == -1) return;
445 
446     LOGD("_get_job_state(): enter");
447 
448     ipp_monitor_t *monitor = NULL;
449     monitor = IMPL(ipp_monitor_t, ifc, this_p);
450 
451     if (this_p != NULL && monitor != NULL && monitor->initialized) {
452         pthread_mutex_lock(&monitor->mutex);
453 
454         do {
455             if (monitor->stop_monitor)
456                 break;
457 
458             ipp_monitor_t *ipp_job;
459             ipp_job = IMPL(ipp_monitor_t, ifc, this_p);
460 
461             if (ipp_job->http == NULL)
462                 break;
463 
464             ipp_jstate_t job_ippstate;
465             ipp_status_t ipp_status = get_JobStatus(ipp_job->http, ipp_job->printer_uri, job_id,
466                                                     job_state_dyn, &job_ippstate,
467                                                     monitor->requesting_user);
468             LOGD("_get_job_state(): Print job State is %d", ipp_status);
469         } while (0);
470         pthread_mutex_unlock(&monitor->mutex);
471     }
472 }