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 volatile 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 if (connect_info->requesting_user_name) {
118 strlcpy(monitor->requesting_user, connect_info->requesting_user_name,
119 sizeof(monitor->requesting_user));
120 }
121 } while (0);
122 }
123
_destroy(const ifc_status_monitor_t * this_p)124 static void _destroy(const ifc_status_monitor_t *this_p) {
125 ipp_monitor_t *monitor;
126 LOGD("_destroy(): enter");
127 do {
128 if (this_p == NULL) {
129 continue;
130 }
131
132 monitor = IMPL(ipp_monitor_t, ifc, this_p);
133 if (monitor->initialized) {
134 pthread_mutex_lock(&monitor->mutex);
135
136 sem_post(&monitor->monitor_sem);
137 sem_destroy(&monitor->monitor_sem);
138
139 pthread_mutex_unlock(&monitor->mutex);
140 pthread_mutex_destroy(&monitor->mutex);
141 monitor->stop_monitor = 1;
142 }
143
144 if (monitor->http != NULL) {
145 httpClose(monitor->http);
146 }
147
148 free(monitor);
149 } while (0);
150 }
151
_get_status(const ifc_status_monitor_t * this_p,printer_state_dyn_t * printer_state_dyn)152 static void _get_status(const ifc_status_monitor_t *this_p,
153 printer_state_dyn_t *printer_state_dyn) {
154 int i;
155 ipp_monitor_t *monitor;
156 ipp_pstate_t printer_state;
157 ipp_status_t ipp_status;
158 LOGD("_get_status(): enter");
159 do {
160 if (printer_state_dyn == NULL) {
161 LOGD("_get_status(): printer_state_dyn is null!");
162 continue;
163 }
164
165 printer_state_dyn->printer_status = PRINT_STATUS_UNKNOWN;
166 printer_state_dyn->printer_reasons[0] = PRINT_STATUS_UNKNOWN;
167 for (i = 0; i <= PRINT_STATUS_MAX_STATE; i++) {
168 printer_state_dyn->printer_reasons[i] = PRINT_STATUS_MAX_STATE;
169 }
170
171 if (this_p == NULL) {
172 LOGE("_get_status(): this_p is null!");
173 continue;
174 }
175
176 monitor = IMPL(ipp_monitor_t, ifc, this_p);
177 if (!monitor->initialized) {
178 LOGE("_get_status(): Monitor is uninitialized");
179 continue;
180 }
181
182 if (monitor->http == NULL) {
183 LOGE("_get_status(): monitor->http is NULL, setting Unable to Connect");
184 printer_state_dyn->printer_reasons[0] = PRINT_STATUS_UNABLE_TO_CONNECT;
185 continue;
186 }
187
188 printer_state_dyn->printer_status = PRINT_STATUS_IDLE;
189 ipp_status = get_PrinterState(monitor->http, monitor->printer_uri, printer_state_dyn,
190 &printer_state);
191 LOGD("_get_status(): ipp_status=%d", ipp_status);
192 debuglist_printerStatus(printer_state_dyn);
193 } while (0);
194 }
195
_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)196 static void _start(const ifc_status_monitor_t *this_p,
197 void (*status_cb)(const printer_state_dyn_t *new_status,
198 const printer_state_dyn_t *old_status, void *status_param),
199 void (*job_state_cb)(const job_state_dyn_t *new_state, void *param),
200 void *param) {
201 int i, job_id = -1;
202 printer_state_dyn_t last_status, curr_status;
203 job_state_dyn_t old_state, new_state;
204 ipp_monitor_t *monitor = NULL;
205
206 LOGD("_start(): enter");
207
208 // initialize our status structures
209 for (i = 0; i <= PRINT_STATUS_MAX_STATE; i++) {
210 curr_status.printer_reasons[i] = PRINT_STATUS_MAX_STATE;
211 last_status.printer_reasons[i] = PRINT_STATUS_MAX_STATE;
212 }
213
214 last_status.printer_status = PRINT_STATUS_UNKNOWN;
215 last_status.printer_reasons[0] = PRINT_STATUS_INITIALIZING;
216 last_status.job_id = 0;
217
218 curr_status.printer_status = PRINT_STATUS_UNKNOWN;
219 curr_status.printer_reasons[0] = PRINT_STATUS_INITIALIZING;
220 curr_status.job_id = 0;
221
222 // send out the first callback
223 if (status_cb != NULL) {
224 (*status_cb)(&curr_status, &last_status, param);
225 }
226
227 /* initialize job status structures */
228 for (i = 0; i <= IPP_JOB_STATE_REASON_MAX_VALUE; i++) {
229 new_state.job_state_reasons[i] = IPP_JOB_STATE_REASON_MAX_VALUE;
230 old_state.job_state_reasons[i] = IPP_JOB_STATE_REASON_MAX_VALUE;
231 }
232
233 old_state.job_state = IPP_JOB_STATE_UNKNOWN;
234 old_state.job_state_reasons[0] = IPP_JOB_STATE_REASON_UNKNOWN;
235
236 new_state.job_state = IPP_JOB_STATE_UNKNOWN;
237 new_state.job_state_reasons[0] = IPP_JOB_STATE_REASON_UNKNOWN;
238
239 do {
240 curr_status.printer_status = PRINT_STATUS_SVC_REQUEST;
241 curr_status.printer_reasons[0] = PRINT_STATUS_UNABLE_TO_CONNECT;
242
243 if (this_p == NULL) {
244 continue;
245 }
246
247 monitor = IMPL(ipp_monitor_t, ifc, this_p);
248 if (!monitor->initialized) {
249 continue;
250 }
251
252 if (monitor->monitor_running) {
253 continue;
254 }
255
256 monitor->stop_monitor = 0;
257 monitor->monitor_running = 1;
258 if (monitor->http == NULL) {
259 if (status_cb != NULL) {
260 (*status_cb)(&curr_status, &last_status, param);
261 }
262 sem_wait(&monitor->monitor_sem);
263
264 last_status.printer_status = PRINT_STATUS_UNKNOWN;
265 last_status.printer_reasons[0] = PRINT_STATUS_SHUTTING_DOWN;
266
267 curr_status.printer_status = PRINT_STATUS_UNKNOWN;
268 curr_status.printer_reasons[0] = PRINT_STATUS_SHUTTING_DOWN;
269 } else {
270 while (!monitor->stop_monitor) {
271 pthread_mutex_lock(&monitor->mutex);
272 curr_status.job_id = job_id;
273 _get_status(this_p, &curr_status);
274 pthread_mutex_unlock(&monitor->mutex);
275 if ((status_cb != NULL) &&
276 (memcmp(&curr_status, &last_status, sizeof(printer_state_dyn_t)) != 0)) {
277 (*status_cb)(&curr_status, &last_status, param);
278 memcpy(&last_status, &curr_status, sizeof(printer_state_dyn_t));
279 }
280
281 // Do not call for job state if thread has been stopped
282 if (job_state_cb != NULL && !monitor->stop_monitor) {
283 pthread_mutex_lock(&monitor->mutex);
284 if (job_id == -1) {
285 job_id = getJobId(monitor->http, monitor->http_resource,
286 monitor->printer_uri, &new_state,
287 monitor->requesting_user);
288 }
289 _get_job_state(this_p, &new_state, job_id);
290 pthread_mutex_unlock(&monitor->mutex);
291
292 if (memcmp(&new_state, &old_state, sizeof(job_state_dyn_t)) != 0) {
293 (*job_state_cb)(&new_state, param);
294 memcpy(&old_state, &new_state, sizeof(job_state_dyn_t));
295 }
296 }
297 sleep(1);
298 }
299 }
300 monitor->monitor_running = 0;
301 } while (0);
302
303 if (status_cb != NULL) {
304 (*status_cb)(&curr_status, &last_status, param);
305 }
306 }
307
_stop(const ifc_status_monitor_t * this_p)308 static void _stop(const ifc_status_monitor_t *this_p) {
309 // request a stop and release the semaphore
310 ipp_monitor_t *monitor;
311 LOGD("_stop(): enter");
312 do {
313 if (this_p == NULL) {
314 continue;
315 }
316
317 monitor = IMPL(ipp_monitor_t, ifc, this_p);
318 if (!monitor->initialized) {
319 continue;
320 }
321
322 sem_post(&monitor->monitor_sem);
323 monitor->stop_monitor = 1;
324 } while (0);
325 }
326
_cancel(const ifc_status_monitor_t * this_p,const char * requesting_user)327 static status_t _cancel(const ifc_status_monitor_t *this_p, const char *requesting_user) {
328 status_t return_value = ERROR;
329 int job_id = -1;
330 ipp_monitor_t *monitor = NULL;
331 ipp_t *request = NULL;
332 ipp_t *response = NULL;
333 ipp_attribute_t *attr;
334
335 LOGD("_cancel(): enter");
336
337 monitor = IMPL(ipp_monitor_t, ifc, this_p);
338 if (this_p != NULL && monitor != NULL && monitor->initialized && !monitor->stop_monitor) {
339 pthread_mutex_lock(&monitor->mutex);
340 do {
341 if (monitor->stop_monitor) {
342 break;
343 }
344
345 request = ippNewRequest(IPP_GET_JOBS);
346 if (request == NULL) {
347 break;
348 }
349
350 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
351 monitor->printer_uri);
352 ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1);
353 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
354 NULL, requesting_user);
355
356 // Requested printer attributes
357 static const char *pattrs[] = {"job-id", "job-state", "job-state-reasons"};
358
359 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes",
360 sizeof(pattrs) / sizeof(pattrs[1]), NULL, pattrs);
361
362 response = ipp_doCupsRequest(monitor->http, request, monitor->http_resource,
363 monitor->printer_uri);
364 if (response == NULL) {
365 ipp_status_t ipp_status = cupsLastError();
366 LOGD("_cancel get job attributes: response is null, ipp_status %d: %s",
367 ipp_status, ippErrorString(ipp_status));
368 return_value = ERROR;
369 } else {
370 attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER);
371 if (attr != NULL) {
372 job_id = ippGetInteger(attr, 0);
373 LOGD("_cancel got job-id: %d", job_id);
374 } else { // We need the job id to attempt a cancel
375 break;
376 }
377
378 attr = ippFindAttribute(response, "job-state", IPP_TAG_ENUM);
379 if (attr != NULL) {
380 ipp_jstate_t jobState = (ipp_jstate_t)ippGetInteger(attr, 0);
381 LOGD("_cancel got job-state: %d", jobState);
382 }
383
384 attr = ippFindAttribute(response, "job-state-reasons", IPP_TAG_KEYWORD);
385 if (attr != NULL) {
386 int idx;
387 for (idx = 0; idx < ippGetCount(attr); idx++) {
388 LOGD("before job-state-reason (%d): %s", idx,
389 ippGetString(attr, idx, NULL));
390 }
391 }
392 }
393 } while (0);
394
395 ippDelete(request);
396 request = NULL;
397 ippDelete(response);
398 response = NULL;
399
400 do {
401 if (job_id == -1) {
402 break;
403 }
404
405 request = ippNewRequest(IPP_CANCEL_JOB);
406 if (request == NULL) {
407 break;
408 }
409
410 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
411 monitor->printer_uri);
412 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", job_id);
413 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
414 "requesting-user-name", NULL, requesting_user);
415
416 if ((response = ipp_doCupsRequest(monitor->http, request, monitor->http_resource,
417 monitor->printer_uri)) == NULL) {
418 ipp_status_t ipp_status = cupsLastError();
419 LOGD("cancel: response is null: ipp_status %d %s", ipp_status,
420 ippErrorString(ipp_status));
421 return_value = ERROR;
422 } else {
423 ipp_status_t ipp_status = cupsLastError();
424 LOGE("IPP_Status for cancel request was %d %s", ipp_status,
425 ippErrorString(ipp_status));
426 attr = ippFindAttribute(response, "job-state-reasons", IPP_TAG_KEYWORD);
427 if (attr != NULL) {
428 int idx;
429 for (idx = 0; ippGetCount(attr); idx++) {
430 LOGD("job-state-reason (%d): %s", idx, ippGetString(attr, idx, NULL));
431 }
432 }
433 return_value = OK;
434 }
435 } while (0);
436
437 ippDelete(request);
438 ippDelete(response);
439
440 if (monitor->initialized) {
441 pthread_mutex_unlock(&monitor->mutex);
442 }
443 }
444 return return_value;
445 }
446
447 /*
448 * Get job state for the given job_id
449 */
_get_job_state(const ifc_status_monitor_t * this_p,job_state_dyn_t * job_state_dyn,int job_id)450 static void _get_job_state(const ifc_status_monitor_t *this_p, job_state_dyn_t *job_state_dyn,
451 int job_id) {
452 if (job_id == -1) return;
453
454 LOGD("_get_job_state(): enter");
455
456 ipp_monitor_t *monitor = NULL;
457 monitor = IMPL(ipp_monitor_t, ifc, this_p);
458
459 if (this_p != NULL && monitor != NULL && monitor->initialized && !monitor->stop_monitor) {
460 pthread_mutex_lock(&monitor->mutex);
461
462 do {
463 if (monitor->stop_monitor)
464 break;
465
466 ipp_monitor_t *ipp_job;
467 ipp_job = IMPL(ipp_monitor_t, ifc, this_p);
468
469 if (ipp_job->http == NULL)
470 break;
471
472 ipp_jstate_t job_ippstate;
473 ipp_status_t ipp_status = get_JobStatus(ipp_job->http, ipp_job->printer_uri, job_id,
474 job_state_dyn, &job_ippstate,
475 monitor->requesting_user);
476 LOGD("_get_job_state(): Print job State is %d", ipp_status);
477 } while (0);
478 pthread_mutex_unlock(&monitor->mutex);
479 }
480 }
481