/** * Copyright (C) 2001-2021 Artifex Software, Inc. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. **/ #include "unistd_.h" #include #include #include #include "ijs.h" #include "ijs_client.h" struct _IjsClientCtx { int fd_from; int child_pid; IjsSendChan send_chan; IjsRecvChan recv_chan; int version; }; IjsClientCtx * ijs_invoke_server (const char *server_cmd) { IjsClientCtx *ctx; int fds_to[2], fds_from[2]; int child_pid; char helo_buf[8] = IJS_HELO_STR; char resp_buf[8]; const char exp_resp_buf[8] = IJS_RESP_STR; ijs_bool ok = TRUE; int nbytes; int version; if (ijs_exec_server(server_cmd, &fds_to[1], &fds_from[0], &child_pid) < 0) return NULL; ctx = (IjsClientCtx *)malloc (sizeof(IjsClientCtx)); ctx->fd_from = fds_from[0]; ctx->child_pid = child_pid; ijs_send_init (&ctx->send_chan, fds_to[1]); ijs_recv_init (&ctx->recv_chan, fds_from[0]); nbytes = write (ctx->send_chan.fd, helo_buf, sizeof(helo_buf)); if (nbytes != sizeof(helo_buf)) ok = FALSE; if (ok) { nbytes = read (ctx->recv_chan.fd, resp_buf, sizeof(resp_buf)); if (nbytes != sizeof(resp_buf) || memcmp (resp_buf, exp_resp_buf, sizeof(resp_buf))) ok = FALSE; } /* exchange version information with server */ if (ok) ok = ijs_client_begin_cmd (ctx, IJS_CMD_PING) >= 0; if (ok) ok = ijs_send_int (&ctx->send_chan, IJS_VERSION) >= 0; if (ok) ok = ijs_client_send_cmd_wait (ctx) >= 0; if (ok) ok = ijs_recv_int (&ctx->recv_chan, &version) >= 0; if (ok) { if (version > IJS_VERSION) version = IJS_VERSION; ctx->version = version; } if (!ok) { close (ctx->send_chan.fd); close (ctx->recv_chan.fd); free (ctx); ctx = NULL; } return ctx; } int ijs_client_begin_cmd (IjsClientCtx *ctx, IjsCommand cmd) { return ijs_send_begin (&ctx->send_chan, cmd); } int ijs_client_send_int (IjsClientCtx *ctx, int val) { return ijs_send_int (&ctx->send_chan, val); } int ijs_client_send_cmd (IjsClientCtx *ctx) { return ijs_send_buf (&ctx->send_chan); } /** * ijs_client_send_cmd_wait: Send command and wait for ack. * @ctx: IJS client context. * * Sends the command in the client context's buffer, and waits for ack. * * Return value: 0 on successful ack, otherwise negative. **/ int ijs_client_send_cmd_wait (IjsClientCtx *ctx) { int status; status = ijs_client_send_cmd (ctx); if (status >= 0) { status = ijs_recv_ack (&ctx->recv_chan); } return status; } /* This is the blocking version; it's not likely to be efficient */ int ijs_client_send_data_wait (IjsClientCtx *ctx, IjsJobId job_id, const char *buf, int size) { int status; ijs_client_begin_cmd (ctx, IJS_CMD_SEND_DATA_BLOCK); ijs_send_int (&ctx->send_chan, job_id); ijs_send_int (&ctx->send_chan, size); status = ijs_client_send_cmd (ctx); if (status) return status; status = write (ctx->send_chan.fd, buf, size); if (status != size) return IJS_EIO; status = ijs_recv_ack (&ctx->recv_chan); return status; } int ijs_client_open (IjsClientCtx *ctx) { ijs_client_begin_cmd (ctx, IJS_CMD_OPEN); return ijs_client_send_cmd_wait (ctx); } int ijs_client_close (IjsClientCtx *ctx) { ijs_client_begin_cmd (ctx, IJS_CMD_CLOSE); return ijs_client_send_cmd_wait (ctx); } int ijs_client_begin_job (IjsClientCtx *ctx, IjsJobId job_id) { ijs_client_begin_cmd (ctx, IJS_CMD_BEGIN_JOB); ijs_send_int (&ctx->send_chan, job_id); return ijs_client_send_cmd_wait (ctx); } int ijs_client_end_job (IjsClientCtx *ctx, IjsJobId job_id) { ijs_client_begin_cmd (ctx, IJS_CMD_END_JOB); ijs_send_int (&ctx->send_chan, job_id); return ijs_client_send_cmd_wait (ctx); } /** * Return value: data block size if nonnegative, or error code if * negative. **/ int ijs_client_list_params (IjsClientCtx *ctx, IjsJobId job_id, char *value, int value_size) { int status; ijs_client_begin_cmd (ctx, IJS_CMD_LIST_PARAMS); ijs_send_int (&ctx->send_chan, job_id); status = ijs_client_send_cmd (ctx); if (status) return status; status = ijs_recv_ack (&ctx->recv_chan); if (status) return status; status = ijs_recv_block (&ctx->recv_chan, value, value_size); return status; } /** * Return value: data block size if nonnegative, or error code if * negative. **/ int ijs_client_enum_param (IjsClientCtx *ctx, IjsJobId job_id, const char *key, char *value, int value_size) { int key_size = strlen (key); int status; ijs_client_begin_cmd (ctx, IJS_CMD_ENUM_PARAM); ijs_send_int (&ctx->send_chan, job_id); status = ijs_send_block (&ctx->send_chan, key, key_size + 1); if (status < 0) return IJS_EIO; status = ijs_client_send_cmd (ctx); if (status) return status; status = ijs_recv_ack (&ctx->recv_chan); if (status) return status; status = ijs_recv_block (&ctx->recv_chan, value, value_size); return status; } int ijs_client_set_param (IjsClientCtx *ctx, IjsJobId job_id, const char *key, const char *value, int value_size) { int key_size = strlen (key); int status; ijs_client_begin_cmd (ctx, IJS_CMD_SET_PARAM); ijs_send_int (&ctx->send_chan, job_id); ijs_send_int (&ctx->send_chan, key_size + 1 + value_size); status = ijs_send_block (&ctx->send_chan, key, key_size+1); if (status) return status; status = ijs_send_block (&ctx->send_chan, value, value_size); if (status) return status; status = ijs_client_send_cmd (ctx); if (status) return status; status = ijs_recv_ack (&ctx->recv_chan); return status; } /** * Return value: data block size if nonnegative, or error code if * negative. **/ int ijs_client_get_param (IjsClientCtx *ctx, IjsJobId job_id, const char *key, char *value, int value_size) { int key_size = strlen (key); int status; ijs_client_begin_cmd (ctx, IJS_CMD_GET_PARAM); ijs_send_int (&ctx->send_chan, job_id); status = ijs_send_block (&ctx->send_chan, key, key_size + 1); if (status < 0) return IJS_EIO; status = ijs_client_send_cmd (ctx); if (status) return status; status = ijs_recv_ack (&ctx->recv_chan); if (status) return status; status = ijs_recv_block (&ctx->recv_chan, value, value_size); return status; } int ijs_client_begin_page (IjsClientCtx *ctx, IjsJobId job_id) { ijs_client_begin_cmd (ctx, IJS_CMD_BEGIN_PAGE); ijs_send_int (&ctx->send_chan, job_id); return ijs_client_send_cmd_wait (ctx); } int ijs_client_end_page (IjsClientCtx *ctx, IjsJobId job_id) { ijs_client_begin_cmd (ctx, IJS_CMD_END_PAGE); ijs_send_int (&ctx->send_chan, job_id); return ijs_client_send_cmd_wait (ctx); } int ijs_client_get_version (IjsClientCtx *ctx) { return ctx->version; }