| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * Copyright (C) 2016 Mopria Alliance, Inc. |
| * Copyright (C) 2013 Hewlett-Packard Development Company, L.P. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef _GNU_SOURCE |
| #define _GNU_SOURCE |
| #endif |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <semaphore.h> |
| #include <fcntl.h> |
| |
| #include "lib_wprint.h" |
| #include "ippstatus_monitor.h" |
| #include "ipphelper.h" |
| |
| #include "cups.h" |
| #include "http-private.h" |
| #include <pthread.h> |
| #include "wprint_debug.h" |
| |
| #define TAG "ippstatus_monitor" |
| |
| static void _init(const ifc_status_monitor_t *this_p, const wprint_connect_info_t *); |
| |
| static void _get_status(const ifc_status_monitor_t *this_p, printer_state_dyn_t *printer_state_dyn); |
| |
| static void _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); |
| |
| static void _stop(const ifc_status_monitor_t *this_p); |
| |
| static status_t _cancel(const ifc_status_monitor_t *this_p, const char *requesting_user); |
| |
| static void _destroy(const ifc_status_monitor_t *this_p); |
| |
| static void _get_job_state(const ifc_status_monitor_t *this_p, job_state_dyn_t *job_state_dyn, |
| int job_id); |
| |
| static const ifc_status_monitor_t _status_ifc = {.init = _init, .get_status = _get_status, |
| .cancel = _cancel, .start = _start, .stop = _stop, .destroy = _destroy,}; |
| |
| typedef struct { |
| unsigned char initialized; |
| http_t *http; |
| char printer_uri[1024]; |
| char http_resource[1024]; |
| volatile unsigned char stop_monitor; |
| unsigned char monitor_running; |
| sem_t monitor_sem; |
| pthread_mutex_t mutex; |
| pthread_mutexattr_t mutexattr; |
| ifc_status_monitor_t ifc; |
| char requesting_user[1024]; |
| } ipp_monitor_t; |
| |
| const ifc_status_monitor_t *ipp_status_get_monitor_ifc(const ifc_wprint_t *wprint_ifc) { |
| ipp_monitor_t *monitor = (ipp_monitor_t *) malloc(sizeof(ipp_monitor_t)); |
| |
| // setup the interface |
| monitor->initialized = 0; |
| monitor->http = NULL; |
| memcpy(&monitor->ifc, &_status_ifc, sizeof(ifc_status_monitor_t)); |
| return &monitor->ifc; |
| } |
| |
| static void _init(const ifc_status_monitor_t *this_p, const wprint_connect_info_t *connect_info) { |
| ipp_monitor_t *monitor; |
| LOGD("_init(): enter"); |
| do { |
| if (this_p == NULL) { |
| continue; |
| } |
| monitor = IMPL(ipp_monitor_t, ifc, this_p); |
| |
| if (monitor->initialized != 0) { |
| sem_post(&monitor->monitor_sem); |
| sem_destroy(&monitor->monitor_sem); |
| |
| pthread_mutex_unlock(&monitor->mutex); |
| pthread_mutex_destroy(&monitor->mutex); |
| } |
| |
| if (monitor->http != NULL) { |
| httpClose(monitor->http); |
| } |
| |
| monitor->http = ipp_cups_connect(connect_info, monitor->printer_uri, |
| sizeof(monitor->printer_uri)); |
| getResourceFromURI(monitor->printer_uri, monitor->http_resource, 1024); |
| |
| monitor->monitor_running = 0; |
| monitor->stop_monitor = 0; |
| |
| pthread_mutexattr_init(&monitor->mutexattr); |
| pthread_mutexattr_settype(&(monitor->mutexattr), PTHREAD_MUTEX_RECURSIVE_NP); |
| pthread_mutex_init(&monitor->mutex, &monitor->mutexattr); |
| sem_init(&monitor->monitor_sem, 0, 0); |
| monitor->initialized = 1; |
| } while (0); |
| } |
| |
| static void _destroy(const ifc_status_monitor_t *this_p) { |
| ipp_monitor_t *monitor; |
| LOGD("_destroy(): enter"); |
| do { |
| if (this_p == NULL) { |
| continue; |
| } |
| |
| monitor = IMPL(ipp_monitor_t, ifc, this_p); |
| if (monitor->initialized) { |
| pthread_mutex_lock(&monitor->mutex); |
| |
| sem_post(&monitor->monitor_sem); |
| sem_destroy(&monitor->monitor_sem); |
| |
| pthread_mutex_unlock(&monitor->mutex); |
| pthread_mutex_destroy(&monitor->mutex); |
| monitor->stop_monitor = 1; |
| } |
| |
| if (monitor->http != NULL) { |
| httpClose(monitor->http); |
| } |
| |
| free(monitor); |
| } while (0); |
| } |
| |
| static void _get_status(const ifc_status_monitor_t *this_p, |
| printer_state_dyn_t *printer_state_dyn) { |
| int i; |
| ipp_monitor_t *monitor; |
| ipp_pstate_t printer_state; |
| ipp_status_t ipp_status; |
| LOGD("_get_status(): enter"); |
| do { |
| if (printer_state_dyn == NULL) { |
| LOGD("_get_status(): printer_state_dyn is null!"); |
| continue; |
| } |
| |
| printer_state_dyn->printer_status = PRINT_STATUS_UNKNOWN; |
| printer_state_dyn->printer_reasons[0] = PRINT_STATUS_UNKNOWN; |
| for (i = 0; i <= PRINT_STATUS_MAX_STATE; i++) { |
| printer_state_dyn->printer_reasons[i] = PRINT_STATUS_MAX_STATE; |
| } |
| |
| if (this_p == NULL) { |
| LOGE("_get_status(): this_p is null!"); |
| continue; |
| } |
| |
| monitor = IMPL(ipp_monitor_t, ifc, this_p); |
| if (!monitor->initialized) { |
| LOGE("_get_status(): Monitor is uninitialized"); |
| continue; |
| } |
| |
| if (monitor->http == NULL) { |
| LOGE("_get_status(): monitor->http is NULL, setting Unable to Connect"); |
| printer_state_dyn->printer_reasons[0] = PRINT_STATUS_UNABLE_TO_CONNECT; |
| continue; |
| } |
| |
| printer_state_dyn->printer_status = PRINT_STATUS_IDLE; |
| ipp_status = get_PrinterState(monitor->http, monitor->printer_uri, printer_state_dyn, |
| &printer_state); |
| LOGD("_get_status(): ipp_status=%d", ipp_status); |
| debuglist_printerStatus(printer_state_dyn); |
| } while (0); |
| } |
| |
| // TODO (b/312004304): job_state_cb code removed due to crash. |
| static void _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) { |
| int i, job_id = -1; |
| printer_state_dyn_t last_status, curr_status; |
| job_state_dyn_t old_state, new_state; |
| ipp_monitor_t *monitor = NULL; |
| |
| LOGD("_start(): enter"); |
| |
| // initialize our status structures |
| for (i = 0; i <= PRINT_STATUS_MAX_STATE; i++) { |
| curr_status.printer_reasons[i] = PRINT_STATUS_MAX_STATE; |
| last_status.printer_reasons[i] = PRINT_STATUS_MAX_STATE; |
| } |
| |
| last_status.printer_status = PRINT_STATUS_UNKNOWN; |
| last_status.printer_reasons[0] = PRINT_STATUS_INITIALIZING; |
| last_status.job_id = 0; |
| |
| curr_status.printer_status = PRINT_STATUS_UNKNOWN; |
| curr_status.printer_reasons[0] = PRINT_STATUS_INITIALIZING; |
| curr_status.job_id = 0; |
| |
| // send out the first callback |
| if (status_cb != NULL) { |
| (*status_cb)(&curr_status, &last_status, param); |
| } |
| |
| /* initialize job status structures */ |
| for (i = 0; i <= IPP_JOB_STATE_REASON_MAX_VALUE; i++) { |
| new_state.job_state_reasons[i] = IPP_JOB_STATE_REASON_MAX_VALUE; |
| old_state.job_state_reasons[i] = IPP_JOB_STATE_REASON_MAX_VALUE; |
| } |
| |
| old_state.job_state = IPP_JOB_STATE_UNKNOWN; |
| old_state.job_state_reasons[0] = IPP_JOB_STATE_REASON_UNKNOWN; |
| |
| new_state.job_state = IPP_JOB_STATE_UNKNOWN; |
| new_state.job_state_reasons[0] = IPP_JOB_STATE_REASON_UNKNOWN; |
| |
| do { |
| curr_status.printer_status = PRINT_STATUS_SVC_REQUEST; |
| curr_status.printer_reasons[0] = PRINT_STATUS_UNABLE_TO_CONNECT; |
| |
| if (this_p == NULL) { |
| continue; |
| } |
| |
| monitor = IMPL(ipp_monitor_t, ifc, this_p); |
| if (!monitor->initialized) { |
| continue; |
| } |
| |
| if (monitor->monitor_running) { |
| continue; |
| } |
| |
| monitor->stop_monitor = 0; |
| monitor->monitor_running = 1; |
| if (monitor->http == NULL) { |
| if (status_cb != NULL) { |
| (*status_cb)(&curr_status, &last_status, param); |
| } |
| sem_wait(&monitor->monitor_sem); |
| |
| last_status.printer_status = PRINT_STATUS_UNKNOWN; |
| last_status.printer_reasons[0] = PRINT_STATUS_SHUTTING_DOWN; |
| |
| curr_status.printer_status = PRINT_STATUS_UNKNOWN; |
| curr_status.printer_reasons[0] = PRINT_STATUS_SHUTTING_DOWN; |
| } else { |
| while (!monitor->stop_monitor) { |
| pthread_mutex_lock(&monitor->mutex); |
| curr_status.job_id = job_id; |
| _get_status(this_p, &curr_status); |
| pthread_mutex_unlock(&monitor->mutex); |
| if ((status_cb != NULL) && |
| (memcmp(&curr_status, &last_status, sizeof(printer_state_dyn_t)) != 0)) { |
| (*status_cb)(&curr_status, &last_status, param); |
| memcpy(&last_status, &curr_status, sizeof(printer_state_dyn_t)); |
| } |
| // TODO (b/312004304): Code removed due to crash. Will add it back with proper mitigation. |
| sleep(1); |
| } |
| } |
| monitor->monitor_running = 0; |
| } while (0); |
| |
| if (status_cb != NULL) { |
| (*status_cb)(&curr_status, &last_status, param); |
| } |
| } |
| |
| static void _stop(const ifc_status_monitor_t *this_p) { |
| // request a stop and release the semaphore |
| ipp_monitor_t *monitor; |
| LOGD("_stop(): enter"); |
| do { |
| if (this_p == NULL) { |
| continue; |
| } |
| |
| monitor = IMPL(ipp_monitor_t, ifc, this_p); |
| if (!monitor->initialized) { |
| continue; |
| } |
| |
| sem_post(&monitor->monitor_sem); |
| monitor->stop_monitor = 1; |
| } while (0); |
| } |
| |
| static status_t _cancel(const ifc_status_monitor_t *this_p, const char *requesting_user) { |
| status_t return_value = ERROR; |
| int job_id = -1; |
| ipp_monitor_t *monitor = NULL; |
| ipp_t *request = NULL; |
| ipp_t *response = NULL; |
| ipp_attribute_t *attr; |
| |
| LOGD("_cancel(): enter"); |
| |
| monitor = IMPL(ipp_monitor_t, ifc, this_p); |
| if (this_p != NULL && monitor != NULL && monitor->initialized && !monitor->stop_monitor) { |
| pthread_mutex_lock(&monitor->mutex); |
| do { |
| if (monitor->stop_monitor) { |
| break; |
| } |
| |
| request = ippNewRequest(IPP_GET_JOBS); |
| if (request == NULL) { |
| break; |
| } |
| |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, |
| monitor->printer_uri); |
| ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1); |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", |
| NULL, requesting_user); |
| |
| // Requested printer attributes |
| static const char *pattrs[] = {"job-id", "job-state", "job-state-reasons"}; |
| |
| ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", |
| sizeof(pattrs) / sizeof(pattrs[1]), NULL, pattrs); |
| |
| response = ipp_doCupsRequest(monitor->http, request, monitor->http_resource, |
| monitor->printer_uri); |
| if (response == NULL) { |
| ipp_status_t ipp_status = cupsLastError(); |
| LOGD("_cancel get job attributes: response is null, ipp_status %d: %s", |
| ipp_status, ippErrorString(ipp_status)); |
| return_value = ERROR; |
| } else { |
| attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER); |
| if (attr != NULL) { |
| job_id = ippGetInteger(attr, 0); |
| LOGD("_cancel got job-id: %d", job_id); |
| } else { // We need the job id to attempt a cancel |
| break; |
| } |
| |
| attr = ippFindAttribute(response, "job-state", IPP_TAG_ENUM); |
| if (attr != NULL) { |
| ipp_jstate_t jobState = (ipp_jstate_t)ippGetInteger(attr, 0); |
| LOGD("_cancel got job-state: %d", jobState); |
| } |
| |
| attr = ippFindAttribute(response, "job-state-reasons", IPP_TAG_KEYWORD); |
| if (attr != NULL) { |
| int idx; |
| for (idx = 0; idx < ippGetCount(attr); idx++) { |
| LOGD("before job-state-reason (%d): %s", idx, |
| ippGetString(attr, idx, NULL)); |
| } |
| } |
| } |
| } while (0); |
| |
| ippDelete(request); |
| request = NULL; |
| ippDelete(response); |
| response = NULL; |
| |
| do { |
| if (job_id == -1) { |
| break; |
| } |
| |
| request = ippNewRequest(IPP_CANCEL_JOB); |
| if (request == NULL) { |
| break; |
| } |
| |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, |
| monitor->printer_uri); |
| ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", job_id); |
| ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, |
| "requesting-user-name", NULL, requesting_user); |
| |
| if ((response = ipp_doCupsRequest(monitor->http, request, monitor->http_resource, |
| monitor->printer_uri)) == NULL) { |
| ipp_status_t ipp_status = cupsLastError(); |
| LOGD("cancel: response is null: ipp_status %d %s", ipp_status, |
| ippErrorString(ipp_status)); |
| return_value = ERROR; |
| } else { |
| ipp_status_t ipp_status = cupsLastError(); |
| LOGE("IPP_Status for cancel request was %d %s", ipp_status, |
| ippErrorString(ipp_status)); |
| attr = ippFindAttribute(response, "job-state-reasons", IPP_TAG_KEYWORD); |
| if (attr != NULL) { |
| int idx; |
| for (idx = 0; ippGetCount(attr); idx++) { |
| LOGD("job-state-reason (%d): %s", idx, ippGetString(attr, idx, NULL)); |
| } |
| } |
| return_value = OK; |
| } |
| } while (0); |
| |
| ippDelete(request); |
| ippDelete(response); |
| |
| if (monitor->initialized) { |
| pthread_mutex_unlock(&monitor->mutex); |
| } |
| } |
| return return_value; |
| } |
| |
| /* |
| * Get job state for the given job_id |
| */ |
| static void _get_job_state(const ifc_status_monitor_t *this_p, job_state_dyn_t *job_state_dyn, |
| int job_id) { |
| if (job_id == -1) return; |
| |
| LOGD("_get_job_state(): enter"); |
| |
| ipp_monitor_t *monitor = NULL; |
| monitor = IMPL(ipp_monitor_t, ifc, this_p); |
| |
| if (this_p != NULL && monitor != NULL && monitor->initialized && !monitor->stop_monitor) { |
| pthread_mutex_lock(&monitor->mutex); |
| |
| do { |
| if (monitor->stop_monitor) |
| break; |
| |
| ipp_monitor_t *ipp_job; |
| ipp_job = IMPL(ipp_monitor_t, ifc, this_p); |
| |
| if (ipp_job->http == NULL) |
| break; |
| |
| ipp_jstate_t job_ippstate; |
| ipp_status_t ipp_status = get_JobStatus(ipp_job->http, ipp_job->printer_uri, job_id, |
| job_state_dyn, &job_ippstate, |
| monitor->requesting_user); |
| LOGD("_get_job_state(): Print job State is %d", ipp_status); |
| } while (0); |
| pthread_mutex_unlock(&monitor->mutex); |
| } |
| } |