1 /**
2  * Copyright (C) 2001-2002 Artifex Software, Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person
5  * obtaining a copy of this software and associated documentation
6  * files (the "Software"), to deal in the Software without
7  * restriction, including without limitation the rights to use, copy,
8  * modify, merge, publish, distribute, sublicense, and/or sell copies
9  * of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23 **/
24 
25 #include "unistd_.h"
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #include "ijs.h"
31 #include "ijs_client.h"
32 
33 struct _IjsClientCtx {
34   int fd_from;
35   int child_pid;
36   IjsSendChan send_chan;
37   IjsRecvChan recv_chan;
38   int version;
39 };
40 
41 IjsClientCtx *
ijs_invoke_server(const char * server_cmd)42 ijs_invoke_server (const char *server_cmd)
43 {
44   IjsClientCtx *ctx;
45   int fds_to[2], fds_from[2];
46   int child_pid;
47   char helo_buf[8] = IJS_HELO_STR;
48   char resp_buf[8];
49   const char exp_resp_buf[8] = IJS_RESP_STR;
50   ijs_bool ok = TRUE;
51   int nbytes;
52   int version;
53 
54   if (ijs_exec_server(server_cmd, &fds_to[1], &fds_from[0], &child_pid) < 0)
55     return NULL;
56 
57   ctx = (IjsClientCtx *)malloc (sizeof(IjsClientCtx));
58   ctx->fd_from = fds_from[0];
59   ctx->child_pid = child_pid;
60   ijs_send_init (&ctx->send_chan, fds_to[1]);
61   ijs_recv_init (&ctx->recv_chan, fds_from[0]);
62 
63   nbytes = write (ctx->send_chan.fd, helo_buf, sizeof(helo_buf));
64   if (nbytes != sizeof(helo_buf))
65     ok = FALSE;
66 
67   if (ok)
68     {
69       nbytes = read (ctx->recv_chan.fd, resp_buf, sizeof(resp_buf));
70       if (nbytes != sizeof(resp_buf) ||
71           memcmp (resp_buf, exp_resp_buf, sizeof(resp_buf)))
72         ok = FALSE;
73     }
74 
75   /* exchange version information with server */
76   if (ok)
77     ok = ijs_client_begin_cmd (ctx, IJS_CMD_PING) >= 0;
78   if (ok)
79     ok = ijs_send_int (&ctx->send_chan, IJS_VERSION) >= 0;
80   if (ok)
81     ok = ijs_client_send_cmd_wait (ctx) >= 0;
82   if (ok)
83     ok = ijs_recv_int (&ctx->recv_chan, &version) >= 0;
84   if (ok)
85     {
86       if (version > IJS_VERSION)
87         version = IJS_VERSION;
88       ctx->version = version;
89     }
90 
91   if (!ok)
92     {
93       close (ctx->send_chan.fd);
94       close (ctx->recv_chan.fd);
95       free (ctx);
96       ctx = NULL;
97     }
98 
99   return ctx;
100 }
101 
102 int
ijs_client_begin_cmd(IjsClientCtx * ctx,IjsCommand cmd)103 ijs_client_begin_cmd (IjsClientCtx *ctx, IjsCommand cmd)
104 {
105   return ijs_send_begin (&ctx->send_chan, cmd);
106 }
107 
108 int
ijs_client_send_int(IjsClientCtx * ctx,int val)109 ijs_client_send_int (IjsClientCtx *ctx, int val)
110 {
111   return ijs_send_int (&ctx->send_chan, val);
112 }
113 
114 int
ijs_client_send_cmd(IjsClientCtx * ctx)115 ijs_client_send_cmd (IjsClientCtx *ctx)
116 {
117   return ijs_send_buf (&ctx->send_chan);
118 }
119 
120 /**
121  * ijs_client_send_cmd_wait: Send command and wait for ack.
122  * @ctx: IJS client context.
123  *
124  * Sends the command in the client context's buffer, and waits for ack.
125  *
126  * Return value: 0 on successful ack, otherwise negative.
127  **/
128 int
ijs_client_send_cmd_wait(IjsClientCtx * ctx)129 ijs_client_send_cmd_wait (IjsClientCtx *ctx)
130 {
131   int status;
132 
133   status = ijs_client_send_cmd (ctx);
134   if (status >= 0)
135     {
136       status = ijs_recv_ack (&ctx->recv_chan);
137     }
138   return status;
139 }
140 
141 /* This is the blocking version; it's not likely to be efficient */
142 int
ijs_client_send_data_wait(IjsClientCtx * ctx,IjsJobId job_id,const char * buf,int size)143 ijs_client_send_data_wait (IjsClientCtx *ctx, IjsJobId job_id,
144                            const char *buf, int size)
145 {
146   int status;
147 
148   ijs_client_begin_cmd (ctx, IJS_CMD_SEND_DATA_BLOCK);
149   ijs_send_int (&ctx->send_chan, job_id);
150   ijs_send_int (&ctx->send_chan, size);
151   status = ijs_client_send_cmd (ctx);
152   if (status)
153     return status;
154   status = write (ctx->send_chan.fd, buf, size);
155   if (status != size)
156     return IJS_EIO;
157   status = ijs_recv_ack (&ctx->recv_chan);
158   return status;
159 }
160 
161 int
ijs_client_open(IjsClientCtx * ctx)162 ijs_client_open (IjsClientCtx *ctx)
163 {
164   ijs_client_begin_cmd (ctx, IJS_CMD_OPEN);
165   return ijs_client_send_cmd_wait (ctx);
166 }
167 
168 int
ijs_client_close(IjsClientCtx * ctx)169 ijs_client_close (IjsClientCtx *ctx)
170 {
171   ijs_client_begin_cmd (ctx, IJS_CMD_CLOSE);
172   return ijs_client_send_cmd_wait (ctx);
173 }
174 
175 int
ijs_client_begin_job(IjsClientCtx * ctx,IjsJobId job_id)176 ijs_client_begin_job (IjsClientCtx *ctx, IjsJobId job_id)
177 {
178   ijs_client_begin_cmd (ctx, IJS_CMD_BEGIN_JOB);
179   ijs_send_int (&ctx->send_chan, job_id);
180   return ijs_client_send_cmd_wait (ctx);
181 }
182 
183 int
ijs_client_end_job(IjsClientCtx * ctx,IjsJobId job_id)184 ijs_client_end_job (IjsClientCtx *ctx, IjsJobId job_id)
185 {
186   ijs_client_begin_cmd (ctx, IJS_CMD_END_JOB);
187   ijs_send_int (&ctx->send_chan, job_id);
188   return ijs_client_send_cmd_wait (ctx);
189 }
190 
191 /**
192  * Return value: data block size if nonnegative, or error code if
193  * negative.
194  **/
195 int
ijs_client_list_params(IjsClientCtx * ctx,IjsJobId job_id,char * value,int value_size)196 ijs_client_list_params (IjsClientCtx *ctx, IjsJobId job_id,
197                       char *value, int value_size)
198 {
199   int status;
200 
201   ijs_client_begin_cmd (ctx, IJS_CMD_LIST_PARAMS);
202   ijs_send_int (&ctx->send_chan, job_id);
203   status = ijs_client_send_cmd (ctx);
204   if (status)
205     return status;
206   status = ijs_recv_ack (&ctx->recv_chan);
207   if (status)
208     return status;
209   status = ijs_recv_block (&ctx->recv_chan, value, value_size);
210   return status;
211 }
212 
213 /**
214  * Return value: data block size if nonnegative, or error code if
215  * negative.
216  **/
217 int
ijs_client_enum_param(IjsClientCtx * ctx,IjsJobId job_id,const char * key,char * value,int value_size)218 ijs_client_enum_param (IjsClientCtx *ctx, IjsJobId job_id,
219                       const char *key,
220                       char *value, int value_size)
221 {
222   int key_size = strlen (key);
223   int status;
224 
225   ijs_client_begin_cmd (ctx, IJS_CMD_ENUM_PARAM);
226   ijs_send_int (&ctx->send_chan, job_id);
227   status = ijs_send_block (&ctx->send_chan, key, key_size + 1);
228   if (status < 0)
229     return IJS_EIO;
230   status = ijs_client_send_cmd (ctx);
231   if (status)
232     return status;
233   status = ijs_recv_ack (&ctx->recv_chan);
234   if (status)
235     return status;
236   status = ijs_recv_block (&ctx->recv_chan, value, value_size);
237   return status;
238 }
239 
240 int
ijs_client_set_param(IjsClientCtx * ctx,IjsJobId job_id,const char * key,const char * value,int value_size)241 ijs_client_set_param (IjsClientCtx *ctx, IjsJobId job_id,
242                       const char *key,
243                       const char *value, int value_size)
244 {
245   int key_size = strlen (key);
246   int status;
247 
248   ijs_client_begin_cmd (ctx, IJS_CMD_SET_PARAM);
249   ijs_send_int (&ctx->send_chan, job_id);
250   ijs_send_int (&ctx->send_chan, key_size + 1 + value_size);
251   status = ijs_send_block (&ctx->send_chan, key, key_size+1);
252   if (status)
253     return status;
254   status = ijs_send_block (&ctx->send_chan, value, value_size);
255   if (status)
256     return status;
257   status = ijs_client_send_cmd (ctx);
258   if (status)
259     return status;
260   status = ijs_recv_ack (&ctx->recv_chan);
261   return status;
262 }
263 
264 /**
265  * Return value: data block size if nonnegative, or error code if
266  * negative.
267  **/
268 int
ijs_client_get_param(IjsClientCtx * ctx,IjsJobId job_id,const char * key,char * value,int value_size)269 ijs_client_get_param (IjsClientCtx *ctx, IjsJobId job_id,
270                       const char *key,
271                       char *value, int value_size)
272 {
273   int key_size = strlen (key);
274   int status;
275 
276   ijs_client_begin_cmd (ctx, IJS_CMD_GET_PARAM);
277   ijs_send_int (&ctx->send_chan, job_id);
278   status = ijs_send_block (&ctx->send_chan, key, key_size + 1);
279   if (status < 0)
280     return IJS_EIO;
281   status = ijs_client_send_cmd (ctx);
282   if (status)
283     return status;
284   status = ijs_recv_ack (&ctx->recv_chan);
285   if (status)
286     return status;
287   status = ijs_recv_block (&ctx->recv_chan, value, value_size);
288   return status;
289 }
290 
291 int
ijs_client_begin_page(IjsClientCtx * ctx,IjsJobId job_id)292 ijs_client_begin_page (IjsClientCtx *ctx, IjsJobId job_id)
293 {
294   ijs_client_begin_cmd (ctx, IJS_CMD_BEGIN_PAGE);
295   ijs_send_int (&ctx->send_chan, job_id);
296   return ijs_client_send_cmd_wait (ctx);
297 }
298 
299 int
ijs_client_end_page(IjsClientCtx * ctx,IjsJobId job_id)300 ijs_client_end_page (IjsClientCtx *ctx, IjsJobId job_id)
301 {
302   ijs_client_begin_cmd (ctx, IJS_CMD_END_PAGE);
303   ijs_send_int (&ctx->send_chan, job_id);
304   return ijs_client_send_cmd_wait (ctx);
305 }
306 
307 int
ijs_client_get_version(IjsClientCtx * ctx)308 ijs_client_get_version (IjsClientCtx *ctx)
309 {
310   return ctx->version;
311 }
312