2 * virnetserverprogram.c: generic network RPC server program
4 * Copyright (C) 2006-2012 Red Hat, Inc.
5 * Copyright (C) 2006 Daniel P. Berrange
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see
19 * <http://www.gnu.org/licenses/>.
24 #include "virnetserverprogram.h"
25 #include "virnetserverclient.h"
31 #include "virthread.h"
33 #define VIR_FROM_THIS VIR_FROM_RPC
35 VIR_LOG_INIT("rpc.netserverprogram");
37 struct _virNetServerProgram
{
42 virNetServerProgramProc
*procs
;
47 static virClass
*virNetServerProgramClass
;
48 static void virNetServerProgramDispose(void *obj
);
50 static int virNetServerProgramOnceInit(void)
52 if (!VIR_CLASS_NEW(virNetServerProgram
, virClassForObject()))
58 VIR_ONCE_GLOBAL_INIT(virNetServerProgram
);
61 virNetServerProgram
*virNetServerProgramNew(unsigned program
,
63 virNetServerProgramProc
*procs
,
66 virNetServerProgram
*prog
;
68 if (virNetServerProgramInitialize() < 0)
71 if (!(prog
= virObjectNew(virNetServerProgramClass
)))
74 prog
->program
= program
;
75 prog
->version
= version
;
77 prog
->nprocs
= nprocs
;
79 VIR_DEBUG("prog=%p", prog
);
85 int virNetServerProgramGetID(virNetServerProgram
*prog
)
91 int virNetServerProgramGetVersion(virNetServerProgram
*prog
)
97 int virNetServerProgramMatches(virNetServerProgram
*prog
,
100 if (prog
->program
== msg
->header
.prog
&&
101 prog
->version
== msg
->header
.vers
)
107 static virNetServerProgramProc
*virNetServerProgramGetProc(virNetServerProgram
*prog
,
110 virNetServerProgramProc
*proc
;
114 if (procedure
>= prog
->nprocs
)
117 proc
= &prog
->procs
[procedure
];
126 virNetServerProgramGetPriority(virNetServerProgram
*prog
,
129 virNetServerProgramProc
*proc
= virNetServerProgramGetProc(prog
, procedure
);
134 return proc
->priority
;
138 virNetServerProgramSendError(unsigned program
,
140 virNetServerClient
*client
,
142 struct virNetMessageError
*rerr
,
147 VIR_DEBUG("prog=%d ver=%d proc=%d type=%d serial=%u msg=%p rerr=%p",
148 program
, version
, procedure
, type
, serial
, msg
, rerr
);
150 virNetMessageSaveError(rerr
);
153 msg
->header
.prog
= program
;
154 msg
->header
.vers
= version
;
155 msg
->header
.proc
= procedure
;
156 msg
->header
.type
= type
;
157 msg
->header
.serial
= serial
;
158 msg
->header
.status
= VIR_NET_ERROR
;
160 if (virNetMessageEncodeHeader(msg
) < 0)
163 if (virNetMessageEncodePayload(msg
, (xdrproc_t
)xdr_virNetMessageError
, rerr
) < 0)
165 xdr_free((xdrproc_t
)xdr_virNetMessageError
, (void*)rerr
);
167 /* Put reply on end of tx queue to send out */
168 if (virNetServerClientSendMessage(client
, msg
) < 0)
174 VIR_WARN("Failed to serialize remote error '%p'", rerr
);
175 xdr_free((xdrproc_t
)xdr_virNetMessageError
, (void*)rerr
);
181 * @client: the client to send the error to
182 * @req: the message this error is in reply to
184 * Send an error message to the client
186 * Returns 0 if the error was sent, -1 upon fatal error
189 virNetServerProgramSendReplyError(virNetServerProgram
*prog
,
190 virNetServerClient
*client
,
192 struct virNetMessageError
*rerr
,
193 struct virNetMessageHeader
*req
)
196 * For data streams, errors are sent back as data streams
197 * For method calls, errors are sent back as method replies
199 return virNetServerProgramSendError(prog
->program
,
205 req
->type
== VIR_NET_STREAM
? VIR_NET_STREAM
: VIR_NET_REPLY
,
210 int virNetServerProgramSendStreamError(virNetServerProgram
*prog
,
211 virNetServerClient
*client
,
213 struct virNetMessageError
*rerr
,
217 return virNetServerProgramSendError(prog
->program
,
228 int virNetServerProgramUnknownError(virNetServerClient
*client
,
230 struct virNetMessageHeader
*req
)
232 virNetMessageError rerr
= { 0 };
234 virReportError(VIR_ERR_RPC
,
235 _("Cannot find program %1$d version %2$d"), req
->prog
, req
->vers
);
237 return virNetServerProgramSendError(req
->prog
,
249 virNetServerProgramDispatchCall(virNetServerProgram
*prog
,
250 virNetServer
*server
,
251 virNetServerClient
*client
,
255 * @server: the unlocked server object
256 * @client: the unlocked client object
257 * @msg: the complete incoming message packet, with header already decoded
259 * This function is intended to be called from worker threads
260 * when an incoming message is ready to be dispatched for
263 * Upon successful return the '@msg' instance will be released
264 * by this function (or more often, reused to send a reply).
265 * Upon failure, the '@msg' must be freed by the caller.
267 * Returns 0 if the message was dispatched, -1 upon fatal error
269 int virNetServerProgramDispatch(virNetServerProgram
*prog
,
270 virNetServer
*server
,
271 virNetServerClient
*client
,
275 virNetMessageError rerr
= { 0 };
277 VIR_DEBUG("prog=%d ver=%d type=%d status=%d serial=%u proc=%d",
278 msg
->header
.prog
, msg
->header
.vers
, msg
->header
.type
,
279 msg
->header
.status
, msg
->header
.serial
, msg
->header
.proc
);
281 /* Check version, etc. */
282 if (msg
->header
.prog
!= prog
->program
) {
283 virReportError(VIR_ERR_RPC
,
284 _("program mismatch (actual %1$x, expected %2$x)"),
285 msg
->header
.prog
, prog
->program
);
289 if (msg
->header
.vers
!= prog
->version
) {
290 virReportError(VIR_ERR_RPC
,
291 _("version mismatch (actual %1$x, expected %2$x)"),
292 msg
->header
.vers
, prog
->version
);
296 switch (msg
->header
.type
) {
298 case VIR_NET_CALL_WITH_FDS
:
299 ret
= virNetServerProgramDispatchCall(prog
, server
, client
, msg
);
303 /* Since stream data is non-acked, async, we may continue to receive
304 * stream packets after we closed down a stream. Just drop & ignore
307 VIR_INFO("Ignoring unexpected stream data serial=%u proc=%d status=%d",
308 msg
->header
.serial
, msg
->header
.proc
, msg
->header
.status
);
309 /* Send a dummy reply to free up 'msg' & unblock client rx */
310 virNetMessageClear(msg
);
311 msg
->header
.type
= VIR_NET_REPLY
;
312 if (virNetServerClientSendMessage(client
, msg
) < 0)
318 case VIR_NET_REPLY_WITH_FDS
:
319 case VIR_NET_MESSAGE
:
320 case VIR_NET_STREAM_HOLE
:
322 virReportError(VIR_ERR_RPC
,
323 _("Unexpected message type %1$u"),
331 if (msg
->header
.type
== VIR_NET_CALL
||
332 msg
->header
.type
== VIR_NET_CALL_WITH_FDS
) {
333 ret
= virNetServerProgramSendReplyError(prog
, client
, msg
, &rerr
, &msg
->header
);
335 /* Send a dummy reply to free up 'msg' & unblock client rx */
336 virNetMessageClear(msg
);
337 msg
->header
.type
= VIR_NET_REPLY
;
338 if (virNetServerClientSendMessage(client
, msg
) < 0)
348 * @server: the unlocked server object
349 * @client: the unlocked client object
350 * @msg: the complete incoming method call, with header already decoded
352 * This method is used to dispatch a message representing an
353 * incoming method call from a client. It decodes the payload
354 * to obtain method call arguments, invokes the method and
355 * then sends a reply packet with the return values
357 * Returns 0 if the reply was sent, or -1 upon fatal error
360 virNetServerProgramDispatchCall(virNetServerProgram
*prog
,
361 virNetServer
*server
,
362 virNetServerClient
*client
,
365 g_autofree
char *arg
= NULL
;
366 g_autofree
char *ret
= NULL
;
368 virNetServerProgramProc
*dispatcher
= NULL
;
369 virNetMessageError rerr
= { 0 };
371 g_autoptr(virIdentity
) identity
= NULL
;
373 if (msg
->header
.status
!= VIR_NET_OK
) {
374 virReportError(VIR_ERR_RPC
,
375 _("Unexpected message status %1$u"),
380 dispatcher
= virNetServerProgramGetProc(prog
, msg
->header
.proc
);
383 virReportError(VIR_ERR_RPC
,
384 _("unknown procedure: %1$d"),
389 /* If the client is not authenticated, don't allow any RPC ops
390 * which are except for authentication ones */
391 if (dispatcher
->needAuth
&&
392 !virNetServerClientIsAuthenticated(client
)) {
393 /* Explicitly *NOT* calling remoteDispatchAuthError() because
394 we want back-compatibility with libvirt clients which don't
395 support the VIR_ERR_AUTH_FAILED error code */
396 virReportError(VIR_ERR_RPC
,
397 "%s", _("authentication required"));
401 arg
= g_new0(char, dispatcher
->arg_len
);
402 ret
= g_new0(char, dispatcher
->ret_len
);
404 if (virNetMessageDecodePayload(msg
, dispatcher
->arg_filter
, arg
) < 0)
407 if (!(identity
= virNetServerClientGetIdentity(client
)))
410 if (virIdentitySetCurrent(identity
) < 0)
414 * When the RPC handler is called:
416 * - Server object is unlocked
417 * - Client object is unlocked
419 * Without locking, it is safe to use:
423 rv
= (dispatcher
->func
)(server
, client
, msg
, &rerr
, arg
, ret
);
425 if (virIdentitySetCurrent(NULL
) < 0)
429 * If rv == 1, this indicates the dispatch func has
430 * populated 'msg' with a list of FDs to return to
433 * Otherwise we must clear out the FDs we got from
434 * the client originally.
438 for (i
= 0; i
< msg
->nfds
; i
++)
439 VIR_FORCE_CLOSE(msg
->fds
[i
]);
447 /* Return header. We're re-using same message object, so
448 * only need to tweak type/status fields */
449 /*msg->header.prog = msg->header.prog;*/
450 /*msg->header.vers = msg->header.vers;*/
451 /*msg->header.proc = msg->header.proc;*/
452 msg
->header
.type
= msg
->nfds
? VIR_NET_REPLY_WITH_FDS
: VIR_NET_REPLY
;
453 /*msg->header.serial = msg->header.serial;*/
454 msg
->header
.status
= VIR_NET_OK
;
456 if (virNetMessageEncodeHeader(msg
) < 0)
460 virNetMessageEncodeNumFDs(msg
) < 0)
463 if (virNetMessageEncodePayload(msg
, dispatcher
->ret_filter
, ret
) < 0)
466 xdr_free(dispatcher
->arg_filter
, arg
);
467 xdr_free(dispatcher
->ret_filter
, ret
);
469 /* Put reply on end of tx queue to send out */
470 return virNetServerClientSendMessage(client
, msg
);
474 xdr_free(dispatcher
->arg_filter
, arg
);
476 xdr_free(dispatcher
->ret_filter
, ret
);
478 /* Bad stuff (de-)serializing message, but we have an
479 * RPC error message we can send back to the client */
480 rv
= virNetServerProgramSendReplyError(prog
, client
, msg
, &rerr
, &msg
->header
);
486 int virNetServerProgramSendStreamData(virNetServerProgram
*prog
,
487 virNetServerClient
*client
,
494 VIR_DEBUG("client=%p msg=%p data=%p len=%zu", client
, msg
, data
, len
);
496 /* Return header. We're reusing same message object, so
497 * only need to tweak type/status fields */
498 msg
->header
.prog
= prog
->program
;
499 msg
->header
.vers
= prog
->version
;
500 msg
->header
.proc
= procedure
;
501 msg
->header
.type
= VIR_NET_STREAM
;
502 msg
->header
.serial
= serial
;
505 * data != NULL + len > 0 => VIR_NET_CONTINUE (Sending back data)
506 * data != NULL + len == 0 => VIR_NET_CONTINUE (Sending read EOF)
507 * data == NULL => VIR_NET_OK (Sending finish handshake confirmation)
509 msg
->header
.status
= data
? VIR_NET_CONTINUE
: VIR_NET_OK
;
511 if (virNetMessageEncodeHeader(msg
) < 0)
514 if (virNetMessageEncodePayloadRaw(msg
, data
, len
) < 0)
517 VIR_DEBUG("Total %zu", msg
->bufferLength
);
519 return virNetServerClientSendMessage(client
, msg
);
523 int virNetServerProgramSendStreamHole(virNetServerProgram
*prog
,
524 virNetServerClient
*client
,
531 virNetStreamHole data
= { 0 };
533 VIR_DEBUG("client=%p msg=%p length=%lld", client
, msg
, length
);
535 data
.length
= length
;
538 msg
->header
.prog
= prog
->program
;
539 msg
->header
.vers
= prog
->version
;
540 msg
->header
.proc
= procedure
;
541 msg
->header
.type
= VIR_NET_STREAM_HOLE
;
542 msg
->header
.serial
= serial
;
543 msg
->header
.status
= VIR_NET_CONTINUE
;
545 if (virNetMessageEncodeHeader(msg
) < 0)
548 if (virNetMessageEncodePayload(msg
,
549 (xdrproc_t
)xdr_virNetStreamHole
,
553 return virNetServerClientSendMessage(client
, msg
);
557 void virNetServerProgramDispose(void *obj G_GNUC_UNUSED
)