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