diff --git a/.x-sc_avoid_write b/.x-sc_avoid_write index e0f94d6027..9e37248b24 100644 --- a/.x-sc_avoid_write +++ b/.x-sc_avoid_write @@ -1,3 +1,4 @@ +^src/libvirt\.c$ ^src/util/util\.c$ ^src/xen/xend_internal\.c$ ^daemon/libvirtd.c$ diff --git a/docs/libvirt-api.xml b/docs/libvirt-api.xml index f5018b9d6c..5981c0ed68 100644 --- a/docs/libvirt-api.xml +++ b/docs/libvirt-api.xml @@ -25,6 +25,7 @@ + @@ -41,13 +42,15 @@ + + - + @@ -77,6 +80,7 @@ + @@ -90,6 +94,7 @@ + @@ -106,9 +111,11 @@ + + @@ -126,11 +133,13 @@ + + @@ -189,6 +198,7 @@ + @@ -201,6 +211,7 @@ + @@ -208,6 +219,7 @@ + @@ -226,6 +238,7 @@ + @@ -235,6 +248,7 @@ + @@ -244,16 +258,20 @@ + + + + @@ -305,9 +323,11 @@ + + @@ -337,8 +357,10 @@ + + @@ -350,6 +372,8 @@ + + @@ -710,6 +734,11 @@ + + + + + @@ -881,6 +910,12 @@ see note above'/> + + + + + + @@ -2657,5 +2692,289 @@ the reference count.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/libvirt-refs.xml b/docs/libvirt-refs.xml index d7255de3fe..e7fbedf8c5 100644 --- a/docs/libvirt-refs.xml +++ b/docs/libvirt-refs.xml @@ -173,6 +173,11 @@ + + + + + @@ -474,6 +479,25 @@ + + + + + + + + + + + + + + + + + + + @@ -654,6 +678,11 @@ + + + + + @@ -959,6 +988,25 @@ + + + + + + + + + + + + + + + + + + + @@ -1040,6 +1088,9 @@ + + + @@ -1078,6 +1129,10 @@ + + + + @@ -1125,6 +1180,7 @@ + @@ -1225,6 +1281,7 @@ + @@ -1317,6 +1374,7 @@ + @@ -1425,6 +1483,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1442,6 +1525,12 @@ + + + + + + @@ -1526,6 +1615,11 @@ + + + + + @@ -1803,6 +1897,25 @@ + + + + + + + + + + + + + + + + + + + @@ -1939,6 +2052,11 @@ + + + + + @@ -2028,6 +2146,7 @@ + @@ -2059,6 +2178,9 @@ + + + @@ -2074,6 +2196,9 @@ + + + @@ -2144,6 +2269,10 @@ + + + + @@ -2214,6 +2343,9 @@ + + + @@ -2248,6 +2380,8 @@ + + @@ -2263,8 +2397,6 @@ - - @@ -2278,6 +2410,10 @@ + + + + @@ -2422,8 +2558,17 @@ + + + + + + + + + @@ -2519,6 +2664,9 @@ + + + @@ -2543,6 +2691,7 @@ + @@ -2564,6 +2713,10 @@ + + + + @@ -2578,6 +2731,9 @@ + + + @@ -2622,6 +2778,7 @@ + @@ -2641,6 +2798,8 @@ + + @@ -2708,8 +2867,6 @@ - - @@ -2749,6 +2906,9 @@ + + + @@ -2784,6 +2944,11 @@ + + + + + @@ -2800,6 +2965,17 @@ + + + + + + + + + + + @@ -2864,6 +3040,11 @@ + + + + + @@ -2884,6 +3065,7 @@ + @@ -2940,6 +3122,9 @@ + + + @@ -2973,6 +3158,8 @@ + + @@ -2982,6 +3169,10 @@ + + + + @@ -2995,6 +3186,8 @@ + + @@ -3003,6 +3196,11 @@ + + + + + @@ -3015,6 +3213,14 @@ + + + + + + + + @@ -3031,8 +3237,14 @@ + + + + + + @@ -3080,6 +3292,9 @@ + + + @@ -3190,8 +3405,20 @@ + + + + + + + + + + + + @@ -3203,6 +3430,8 @@ + + @@ -3243,6 +3472,7 @@ + @@ -3251,6 +3481,12 @@ + + + + + + @@ -3268,9 +3504,23 @@ + + + + + + + + + + + + + + @@ -3279,6 +3529,11 @@ + + + + + @@ -3296,6 +3551,8 @@ + + @@ -3316,33 +3573,10 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + @@ -3354,6 +3588,8 @@ + + @@ -3383,6 +3619,12 @@ + + + + + + @@ -3449,6 +3691,10 @@ + + + + @@ -3456,6 +3702,10 @@ + + + + @@ -3465,6 +3715,12 @@ + + + + + + @@ -3474,6 +3730,7 @@ + @@ -3507,6 +3764,9 @@ + + + @@ -3529,6 +3789,10 @@ + + + + @@ -3537,6 +3801,12 @@ + + + + + + @@ -3554,10 +3824,20 @@ + + + + + + + + + + @@ -3577,6 +3857,10 @@ + + + + @@ -3584,10 +3868,15 @@ + + + + + @@ -3611,6 +3900,7 @@ + @@ -3633,6 +3923,8 @@ + + @@ -3705,25 +3997,6 @@ - - - - - - - - - - - - - - - - - - - @@ -3767,6 +4040,8 @@ + + @@ -3779,10 +4054,20 @@ + + + + + + + + + + @@ -3847,6 +4132,9 @@ + + + @@ -3906,6 +4194,9 @@ + + + @@ -3964,11 +4255,23 @@ + + + + + + + + + + + + @@ -3980,6 +4283,9 @@ + + + @@ -4028,6 +4334,11 @@ + + + + + @@ -4061,9 +4372,23 @@ + + + + + + + + + + + + + + @@ -4081,6 +4406,7 @@ + @@ -4100,6 +4426,7 @@ + @@ -4108,6 +4435,7 @@ + @@ -4120,6 +4448,10 @@ + + + + @@ -4201,6 +4533,7 @@ + @@ -4215,6 +4548,7 @@ + @@ -4234,6 +4568,11 @@ + + + + + @@ -4241,6 +4580,7 @@ + @@ -4248,6 +4588,8 @@ + + @@ -4265,6 +4607,7 @@ + @@ -4274,11 +4617,16 @@ + + + + + @@ -4315,34 +4663,13 @@ - - - - - - - - - - - - - - - - - - - - - - - - + + + @@ -4355,6 +4682,7 @@ + @@ -4388,6 +4716,7 @@ + @@ -4431,9 +4760,27 @@ + + + + + + + + + + + + + + + + + + @@ -4456,6 +4803,7 @@ + @@ -4486,6 +4834,13 @@ + + + + + + + @@ -4505,9 +4860,16 @@ + + + + + + + @@ -4567,6 +4929,12 @@ + + + + + + @@ -4664,6 +5032,8 @@ + + @@ -4677,6 +5047,9 @@ + + + @@ -4699,6 +5072,7 @@ + @@ -4713,6 +5087,8 @@ + + @@ -4734,9 +5110,15 @@ + + + + + + @@ -4768,12 +5150,20 @@ + + + + + + + + @@ -4786,6 +5176,12 @@ + + + + + + @@ -4902,6 +5298,8 @@ + + @@ -4940,6 +5338,8 @@ + + @@ -4953,6 +5353,12 @@ + + + + + + @@ -5037,6 +5443,8 @@ + + @@ -5044,6 +5452,12 @@ + + + + + + @@ -5109,6 +5523,10 @@ + + + + @@ -5163,25 +5581,6 @@ - - - - - - - - - - - - - - - - - - - @@ -5269,6 +5668,8 @@ + + @@ -5293,6 +5694,14 @@ + + + + + + + + @@ -5323,6 +5732,12 @@ + + + + + + @@ -5339,9 +5754,12 @@ + + + @@ -5352,6 +5770,8 @@ + + @@ -5382,6 +5802,11 @@ + + + + + @@ -5406,6 +5831,9 @@ + + + @@ -5416,6 +5844,8 @@ + + @@ -5438,6 +5868,9 @@ + + + @@ -5445,6 +5878,7 @@ + @@ -5457,6 +5891,8 @@ + + @@ -5469,6 +5905,9 @@ + + + @@ -5495,6 +5934,8 @@ + + @@ -5504,6 +5945,8 @@ + + @@ -5515,6 +5958,10 @@ + + + + @@ -5530,6 +5977,8 @@ + + @@ -5544,6 +5993,8 @@ + + @@ -5552,6 +6003,7 @@ + @@ -5571,6 +6023,7 @@ + @@ -5584,12 +6037,18 @@ + + + + + + @@ -5655,11 +6114,13 @@ + + @@ -5681,6 +6142,8 @@ + + @@ -5691,6 +6154,7 @@ + @@ -5720,6 +6184,9 @@ + + + @@ -5752,6 +6219,10 @@ + + + + @@ -5780,6 +6251,9 @@ + + + @@ -5804,6 +6278,9 @@ + + + @@ -5811,12 +6288,17 @@ + + + + + @@ -5826,6 +6308,8 @@ + + @@ -5839,11 +6323,13 @@ + + @@ -5890,6 +6376,13 @@ + + + + + + + @@ -5897,6 +6390,12 @@ + + + + + + @@ -5915,10 +6414,16 @@ + + + + + + @@ -5942,6 +6447,8 @@ + + @@ -5964,12 +6471,17 @@ + + + + + @@ -6020,9 +6532,17 @@ + + + + + + + + @@ -6037,6 +6557,10 @@ + + + + @@ -6062,6 +6586,7 @@ + @@ -6139,6 +6664,10 @@ + + + + @@ -6165,6 +6694,9 @@ + + + @@ -6176,6 +6708,7 @@ + @@ -6284,12 +6817,30 @@ + + + + + + + + + + + + + + + + + + @@ -6329,33 +6880,12 @@ + - - - - - - - - - - - - - - - - - - - - - - @@ -6375,12 +6905,23 @@ + + + + + + + + + + + @@ -6402,6 +6943,9 @@ + + + @@ -6500,6 +7044,7 @@ + @@ -6537,6 +7082,13 @@ + + + + + + + @@ -6551,12 +7103,23 @@ + + + + + + + + + + + @@ -6564,6 +7127,7 @@ + @@ -6609,12 +7173,22 @@ + + + + + + + + + + @@ -6652,6 +7226,10 @@ + + + + @@ -6664,6 +7242,8 @@ + + @@ -6673,36 +7253,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -6723,6 +7273,9 @@ + + + @@ -6775,6 +7328,10 @@ + + + + @@ -6788,6 +7345,8 @@ + + @@ -6796,10 +7355,22 @@ + + + + + + + + + + + + @@ -6925,6 +7496,10 @@ + + + + @@ -6946,16 +7521,31 @@ + + + + + + + + + + + + + + + @@ -7035,6 +7625,7 @@ + @@ -7049,21 +7640,6 @@ - - - - - - - - - - - - - - - @@ -7123,6 +7699,9 @@ + + + @@ -7145,6 +7724,11 @@ + + + + + @@ -7227,6 +7811,7 @@ + @@ -7334,6 +7919,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -7365,6 +8008,10 @@ + + + + @@ -7382,6 +8029,9 @@ + + + @@ -7399,6 +8049,8 @@ + + @@ -7426,6 +8078,8 @@ + + @@ -7455,6 +8109,8 @@ + + @@ -7502,6 +8158,9 @@ + + + @@ -7510,10 +8169,13 @@ + + + @@ -7559,13 +8221,14 @@ + - - - + + + diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 6028d5fa9e..4e63e48980 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -110,6 +110,24 @@ typedef enum { VIR_DOMAIN_NONE = 0 } virDomainCreateFlags; + + +/** + * virStream: + * + * a virStream is a private structure representing a data stream. + */ +typedef struct _virStream virStream; + +/** + * virStreamPtr: + * + * a virStreamPtr is pointer to a virStream private structure, this is the + * type used to reference a data stream in the API. + */ +typedef virStream *virStreamPtr; + + /** * VIR_SECURITY_LABEL_BUFLEN: * @@ -1502,6 +1520,127 @@ int virSecretUndefine (virSecretPtr secret); int virSecretRef (virSecretPtr secret); int virSecretFree (virSecretPtr secret); +typedef enum { + VIR_STREAM_NONBLOCK = (1 << 0), +} virStreamFlags; + +virStreamPtr virStreamNew(virConnectPtr conn, + unsigned int flags); +int virStreamRef(virStreamPtr st); + +int virStreamSend(virStreamPtr st, + const char *data, + size_t nbytes); + +int virStreamRecv(virStreamPtr st, + char *data, + size_t nbytes); + + +/** + * virStreamSourceFunc: + * + * @st: the stream object + * @data: preallocated array to be filled with data + * @nbytes: size of the data array + * @opaque: optional application provided data + * + * The virStreamSourceFunc callback is used together + * with the virStreamSendAll function for libvirt to + * obtain the data that is to be sent. + * + * The callback will be invoked multiple times, + * fetching data in small chunks. The application + * should fill the 'data' array with upto 'nbytes' + * of data and then return the number actual number + * of bytes. The callback will continue to be + * invoked until it indicates the end of the source + * has been reached by returning 0. A return value + * of -1 at any time will abort the send operation + * + * Returns the number of bytes filled, 0 upon end + * of file, or -1 upon error + */ +typedef int (*virStreamSourceFunc)(virStreamPtr st, + char *data, + size_t nbytes, + void *opaque); + +int virStreamSendAll(virStreamPtr st, + virStreamSourceFunc handler, + void *opaque); + +/** + * virStreamSinkFunc: + * + * @st: the stream object + * @data: preallocated array to be filled with data + * @nbytes: size of the data array + * @opaque: optional application provided data + * + * The virStreamSinkFunc callback is used together + * with the virStreamRecvAll function for libvirt to + * provide the data that has been received. + * + * The callback will be invoked multiple times, + * providing data in small chunks. The application + * should consume up 'nbytes' from the 'data' array + * of data and then return the number actual number + * of bytes consumed. The callback will continue to be + * invoked until it indicates the end of the stream + * has been reached. A return value of -1 at any time + * will abort the receive operation + * + * Returns the number of bytes consumed or -1 upon + * error + */ +typedef int (*virStreamSinkFunc)(virStreamPtr st, + const char *data, + size_t nbytes, + void *opaque); + +int virStreamRecvAll(virStreamPtr st, + virStreamSinkFunc handler, + void *opaque); + +typedef enum { + VIR_STREAM_EVENT_READABLE = (1 << 0), + VIR_STREAM_EVENT_WRITABLE = (1 << 1), + VIR_STREAM_EVENT_ERROR = (1 << 2), + VIR_STREAM_EVENT_HANGUP = (1 << 3), +} virStreamEventType; + + +/** + * virStreamEventCallback: + * + * @stream: stream on which the event occurred + * @events: bitset of events from virEventHandleType constants + * @opaque: user data registered with handle + * + * Callback for receiving stream events. The callback will + * be invoked once for each event which is pending. + */ +typedef void (*virStreamEventCallback)(virStreamPtr stream, int events, void *opaque); + +int virStreamEventAddCallback(virStreamPtr stream, + int events, + virStreamEventCallback cb, + void *opaque, + virFreeCallback ff); + +int virStreamEventUpdateCallback(virStreamPtr stream, + int events); + +int virStreamEventRemoveCallback(virStreamPtr stream); + + +int virStreamFinish(virStreamPtr st); +int virStreamAbort(virStreamPtr st); + +int virStreamFree(virStreamPtr st); + + #ifdef __cplusplus } #endif diff --git a/python/Makefile.am b/python/Makefile.am index 95ae84d2e4..cda655981e 100644 --- a/python/Makefile.am +++ b/python/Makefile.am @@ -13,7 +13,9 @@ DOCS_DIR = $(datadir)/doc/libvirt-python-$(LIBVIRT_VERSION) DOCS = ${srcdir}/TODO -CLASSES_EXTRA = libvirt-override-virConnect.py +CLASSES_EXTRA = \ + libvirt-override-virConnect.py \ + libvirt-override-virStream.py EXTRA_DIST = \ generator.py \ diff --git a/python/generator.py b/python/generator.py index 178a415183..48ad14b855 100755 --- a/python/generator.py +++ b/python/generator.py @@ -282,6 +282,11 @@ py_types = { 'const virSecretPtr': ('O', "virSecret", "virSecretPtr", "virSecretPtr"), 'virSecret *': ('O', "virSecret", "virSecretPtr", "virSecretPtr"), 'const virSecret *': ('O', "virSecret", "virSecretPtr", "virSecretPtr"), + + 'virStreamPtr': ('O', "virStream", "virStreamPtr", "virStreamPtr"), + 'const virStreamPtr': ('O', "virStream", "virStreamPtr", "virStreamPtr"), + 'virStream *': ('O', "virStream", "virStreamPtr", "virStreamPtr"), + 'const virStream *': ('O', "virStream", "virStreamPtr", "virStreamPtr"), } py_return_types = { @@ -338,6 +343,8 @@ skip_impl = ( 'virSecretGetUUID', 'virSecretGetUUIDString', 'virSecretLookupByUUID', + 'virStreamRecv', + 'virStreamSend', 'virStoragePoolGetUUID', 'virStoragePoolGetUUIDString', 'virStoragePoolLookupByUUID', @@ -373,6 +380,11 @@ skip_function = ( 'virConnectDomainEventDeregister', # overridden in virConnect.py 'virSaveLastError', # We have our own python error wrapper 'virFreeError', # Only needed if we use virSaveLastError + 'virStreamEventAddCallback', + 'virStreamRecvAll', + 'virStreamSendAll', + 'virStreamRef', + 'virStreamFree', ) @@ -643,6 +655,8 @@ classes_type = { "virNodeDevice *": ("._o", "virNodeDevice(self, _obj=%s)", "virNodeDevice"), "virSecretPtr": ("._o", "virSecret(self, _obj=%s)", "virSecret"), "virSecret *": ("._o", "virSecret(self, _obj=%s)", "virSecret"), + "virStreamPtr": ("._o", "virStream(self, _obj=%s)", "virStream"), + "virStream *": ("._o", "virStream(self, _obj=%s)", "virStream"), "virConnectPtr": ("._o", "virConnect(_obj=%s)", "virConnect"), "virConnect *": ("._o", "virConnect(_obj=%s)", "virConnect"), } @@ -652,7 +666,8 @@ converter_type = { primary_classes = ["virDomain", "virNetwork", "virInterface", "virStoragePool", "virStorageVol", - "virConnect", "virNodeDevice", "virSecret" ] + "virConnect", "virNodeDevice", "virSecret", + "virStream"] classes_ancestor = { } @@ -663,7 +678,9 @@ classes_destructors = { "virStoragePool": "virStoragePoolFree", "virStorageVol": "virStorageVolFree", "virNodeDevice" : "virNodeDeviceFree", - "virSecret": "virSecretFree" + "virSecret": "virSecretFree", + # We hand-craft __del__ for this one + #"virStream": "virStreamFree", } functions_noexcept = { @@ -782,6 +799,11 @@ def nameFixup(name, classe, type, file): elif name[0:9] == 'virSecret': func = name[9:] func = string.lower(func[0:1]) + func[1:] + elif name[0:12] == 'virStreamNew': + func = "newStream" + elif name[0:9] == 'virStream': + func = name[9:] + func = string.lower(func[0:1]) + func[1:] elif name[0:17] == "virStoragePoolGet": func = name[17:] func = string.lower(func[0:1]) + func[1:] @@ -1059,7 +1081,8 @@ def buildWrappers(): classes_ancestor[classname])) else: classes.write("class %s:\n" % (classname)) - if classname in [ "virDomain", "virNetwork", "virInterface", "virStoragePool", "virStorageVol", "virNodeDevice", "virSecret" ]: + if classname in [ "virDomain", "virNetwork", "virInterface", "virStoragePool", + "virStorageVol", "virNodeDevice", "virSecret","virStream" ]: classes.write(" def __init__(self, conn, _obj=None):\n") else: classes.write(" def __init__(self, _obj=None):\n") @@ -1067,7 +1090,8 @@ def buildWrappers(): list = reference_keepers[classname] for ref in list: classes.write(" self.%s = None\n" % ref[1]) - if classname in [ "virDomain", "virNetwork", "virInterface", "virNodeDevice", "virSecret" ]: + if classname in [ "virDomain", "virNetwork", "virInterface", + "virNodeDevice", "virSecret", "virStream" ]: classes.write(" self._conn = conn\n") elif classname in [ "virStorageVol", "virStoragePool" ]: classes.write(" self._conn = conn\n" + \ diff --git a/python/libvirt-override-virStream.py b/python/libvirt-override-virStream.py new file mode 100644 index 0000000000..f50a7efe90 --- /dev/null +++ b/python/libvirt-override-virStream.py @@ -0,0 +1,20 @@ + def __del__(self): + try: + if self.cb: + libvirtmod.virStreamEventRemoveCallback(self._o) + except AttributeError: + pass + + if self._o != None: + libvirtmod.virStreamFree(self._o) + self._o = None + + def eventAddCallback(self, cb, opaque): + """ """ + try: + self.cb = cb + self.opaque = opaque + ret = libvirtmod.virStreamEventAddCallback(self._o, self) + if ret == -1: raise libvirtError ('virStreamEventAddCallback() failed', conn=self._conn) + except AttributeError: + pass diff --git a/python/typewrappers.c b/python/typewrappers.c index 0d8ac97583..9ba99de0db 100644 --- a/python/typewrappers.c +++ b/python/typewrappers.c @@ -206,6 +206,19 @@ libvirt_virSecretPtrWrap(virSecretPtr node) return (ret); } +PyObject * +libvirt_virStreamPtrWrap(virStreamPtr node) +{ + PyObject *ret; + + if (node == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + ret = PyCObject_FromVoidPtrAndDesc(node, (char *) "virStreamPtr", NULL); + return (ret); +} + PyObject * libvirt_virEventHandleCallbackWrap(virEventHandleCallback node) { diff --git a/python/typewrappers.h b/python/typewrappers.h index 99d5805f2b..61f72490be 100644 --- a/python/typewrappers.h +++ b/python/typewrappers.h @@ -92,6 +92,15 @@ typedef struct { } PyvirSecret_Object; +#define PyvirStream_Get(v) (((v) == Py_None) ? NULL : \ + (((PyvirStream_Object *)(v))->obj)) + +typedef struct { + PyObject_HEAD + virStreamPtr obj; +} PyvirStream_Object; + + #define PyvirEventHandleCallback_Get(v) (((v) == Py_None) ? NULL : \ (((PyvirEventHandleCallback_Object *)(v))->obj)) @@ -144,6 +153,7 @@ PyObject * libvirt_virFreeCallbackWrap(virFreeCallback node); PyObject * libvirt_virVoidPtrWrap(void* node); PyObject * libvirt_virNodeDevicePtrWrap(virNodeDevicePtr node); PyObject * libvirt_virSecretPtrWrap(virSecretPtr node); +PyObject * libvirt_virStreamPtrWrap(virStreamPtr node); /* Provide simple macro statement wrappers (adapted from GLib, in turn from Perl): diff --git a/src/datatypes.c b/src/datatypes.c index 530076c0ba..6741b9e4dd 100644 --- a/src/datatypes.c +++ b/src/datatypes.c @@ -1162,6 +1162,7 @@ virUnrefNodeDevice(virNodeDevicePtr dev) { return (refs); } + /** * virGetSecret: * @conn: the hypervisor connection @@ -1297,3 +1298,61 @@ virUnrefSecret(virSecretPtr secret) { virMutexUnlock(&secret->conn->lock); return refs; } + +virStreamPtr virGetStream(virConnectPtr conn) { + virStreamPtr ret = NULL; + + virMutexLock(&conn->lock); + + if (VIR_ALLOC(ret) < 0) { + virReportOOMError(conn); + goto error; + } + ret->magic = VIR_STREAM_MAGIC; + ret->conn = conn; + conn->refs++; + ret->refs++; + virMutexUnlock(&conn->lock); + return(ret); + +error: + virMutexUnlock(&conn->lock); + VIR_FREE(ret); + return(NULL); +} + +static void +virReleaseStream(virStreamPtr st) { + virConnectPtr conn = st->conn; + DEBUG("release dev %p", st); + + st->magic = -1; + VIR_FREE(st); + + DEBUG("unref connection %p %d", conn, conn->refs); + conn->refs--; + if (conn->refs == 0) { + virReleaseConnect(conn); + /* Already unlocked mutex */ + return; + } + + virMutexUnlock(&conn->lock); +} + +int virUnrefStream(virStreamPtr st) { + int refs; + + virMutexLock(&st->conn->lock); + DEBUG("unref stream %p %d", st, st->refs); + st->refs--; + refs = st->refs; + if (refs == 0) { + virReleaseStream(st); + /* Already unlocked mutex */ + return (0); + } + + virMutexUnlock(&st->conn->lock); + return (refs); +} diff --git a/src/datatypes.h b/src/datatypes.h index a33c365a6a..afb51dcbd6 100644 --- a/src/datatypes.h +++ b/src/datatypes.h @@ -109,6 +109,17 @@ #define VIR_IS_CONNECTED_SECRET(obj) (VIR_IS_SECRET(obj) && VIR_IS_CONNECT((obj)->conn)) +/** + * VIR_STREAM_MAGIC: + * + * magic value used to protect the API when pointers to stream structures + * are passed down by the users. + */ +#define VIR_STREAM_MAGIC 0x1DEAD666 +#define VIR_IS_STREAM(obj) ((obj) && (obj)->magic==VIR_STREAM_MAGIC) +#define VIR_IS_CONNECTED_STREAM(obj) (VIR_IS_STREAM(obj) && VIR_IS_CONNECT((obj)->conn)) + + /** * _virConnect: * @@ -261,6 +272,25 @@ struct _virSecret { }; +typedef int (*virStreamAbortFunc)(virStreamPtr, void *opaque); +typedef int (*virStreamFinishFunc)(virStreamPtr, void *opaque); + +/** + * _virStream: + * + * Internal structure associated with an input stream + */ +struct _virStream { + unsigned int magic; + virConnectPtr conn; + int refs; + int flags; + + virStreamDriverPtr driver; + void *privateData; +}; + + /************************************************************************ * * * API for domain/connections (de)allocations and lookups * @@ -303,4 +333,7 @@ virSecretPtr virGetSecret(virConnectPtr conn, const char *usageID); int virUnrefSecret(virSecretPtr secret); +virStreamPtr virGetStream(virConnectPtr conn); +int virUnrefStream(virStreamPtr st); + #endif diff --git a/src/driver.h b/src/driver.h index d4f972f3e9..6a3dcc2891 100644 --- a/src/driver.h +++ b/src/driver.h @@ -879,6 +879,41 @@ struct _virSecretDriver { virDrvSecretUndefine undefine; }; + +typedef struct _virStreamDriver virStreamDriver; +typedef virStreamDriver *virStreamDriverPtr; + +typedef int (*virDrvStreamSend)(virStreamPtr st, + const char *data, + size_t nbytes); +typedef int (*virDrvStreamRecv)(virStreamPtr st, + char *data, + size_t nbytes); + +typedef int (*virDrvStreamEventAddCallback)(virStreamPtr stream, + int events, + virStreamEventCallback cb, + void *opaque, + virFreeCallback ff); + +typedef int (*virDrvStreamEventUpdateCallback)(virStreamPtr stream, + int events); +typedef int (*virDrvStreamEventRemoveCallback)(virStreamPtr stream); +typedef int (*virDrvStreamFinish)(virStreamPtr st); +typedef int (*virDrvStreamAbort)(virStreamPtr st); + + +struct _virStreamDriver { + virDrvStreamSend streamSend; + virDrvStreamRecv streamRecv; + virDrvStreamEventAddCallback streamAddCallback; + virDrvStreamEventUpdateCallback streamUpdateCallback; + virDrvStreamEventRemoveCallback streamRemoveCallback; + virDrvStreamFinish streamFinish; + virDrvStreamAbort streamAbort; +}; + + /* * Registration * TODO: also need ways to (des)activate a given driver diff --git a/src/libvirt.c b/src/libvirt.c index f164f60650..48e7b5bcb2 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -561,6 +561,10 @@ virLibNodeDeviceError(virNodeDevicePtr dev, virErrorNumber error, errmsg, info, NULL, 0, 0, errmsg, info); } +#define virLibStreamError(conn, code, fmt...) \ + virReportErrorHelper(conn, VIR_FROM_NONE, code, __FILE__, \ + __FUNCTION__, __LINE__, fmt) + /** * virLibSecretError: * @secret: the secret if available @@ -9394,3 +9398,700 @@ virSecretFree(virSecretPtr secret) return -1; return 0; } + + +/** + * virStreamNew: + * @conn: pointer to the connection + * @flags: control features of the stream + * + * Creates a new stream object which can be used to perform + * streamed I/O with other public API function. + * + * When no longer needed, a stream object must be released + * with virStreamFree. If a data stream has been used, + * then the application must call virStreamFinish or + * virStreamAbort before free'ing to, in order to notify + * the driver of termination. + * + * If a non-blocking data stream is required passed + * VIR_STREAM_NONBLOCK for flags, otherwise pass 0. + * + * Returns the new stream, or NULL upon error + */ +virStreamPtr +virStreamNew(virConnectPtr conn, + unsigned int flags) +{ + virStreamPtr st; + + DEBUG("conn=%p, flags=%u", conn, flags); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (NULL); + } + + st = virGetStream(conn); + if (st) + st->flags = flags; + + return st; +} + + +/** + * virStreamRef: + * @stream: pointer to the stream + * + * Increment the reference count on the stream. For each + * additional call to this method, there shall be a corresponding + * call to virStreamFree to release the reference count, once + * the caller no longer needs the reference to this object. + * + * Returns 0 in case of success, -1 in case of failure + */ +int +virStreamRef(virStreamPtr stream) +{ + if ((!VIR_IS_CONNECTED_STREAM(stream))) { + virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__); + return(-1); + } + virMutexLock(&stream->conn->lock); + DEBUG("stream=%p refs=%d", stream, stream->refs); + stream->refs++; + virMutexUnlock(&stream->conn->lock); + return 0; +} + + +/** + * virStreamSend: + * @stream: pointer to the stream object + * @data: buffer to write to stream + * @nbytes: size of @data buffer + * + * Write a series of bytes to the stream. This method may + * block the calling application for an arbitrary amount + * of time. Once an application has finished sending data + * it should call virStreamFinish to wait for succesful + * confirmation from the driver, or detect any error + * + * This method may not be used if a stream source has been + * registered + * + * Errors are not guaranteed to be reported synchronously + * with the call, but may instead be delayed until a + * subsequent call. + * + * A example using this with a hypothetical file upload + * API looks like + * + * virStreamPtr st = virStreamNew(conn, 0); + * int fd = open("demo.iso", O_RDONLY) + * + * virConnectUploadFile(conn, "demo.iso", st); + * + * while (1) { + * char buf[1024]; + * int got = read(fd, buf, 1024); + * if (got < 0) { + * virStreamAbort(st); + * break; + * } + * if (got == 0) { + * virStreamFinish(st); + * break; + * } + * int offset = 0; + * while (offset < got) { + * int sent = virStreamSend(st, buf+offset, got-offset) + * if (sent < 0) { + * virStreamAbort(st); + * goto done; + * } + * offset += sent; + * } + * } + * if (virStreamFinish(st) < 0) + * ... report an error .... + * done: + * virStreamFree(st); + * close(fd); + * + * Returns the number of bytes written, which may be less + * than requested. + * + * Returns -1 upon error, at which time the stream will + * be marked as aborted, and the caller should now release + * the stream with virStreamFree. + * + * Returns -2 if the outgoing transmit buffers are full & + * the stream is marked as non-blocking. + */ +int virStreamSend(virStreamPtr stream, + const char *data, + size_t nbytes) +{ + DEBUG("stream=%p, data=%p, nbytes=%zi", stream, data, nbytes); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_STREAM(stream)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if (stream->driver && + stream->driver->streamSend) { + int ret; + ret = (stream->driver->streamSend)(stream, data, nbytes); + if (ret == -2) + return -2; + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(stream->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(stream->conn); + return -1; +} + + +/** + * virStreamRecv: + * @stream: pointer to the stream object + * @data: buffer to write to stream + * @nbytes: size of @data buffer + * + * Write a series of bytes to the stream. This method may + * block the calling application for an arbitrary amount + * of time. + * + * Errors are not guaranteed to be reported synchronously + * with the call, but may instead be delayed until a + * subsequent call. + * + * A example using this with a hypothetical file download + * API looks like + * + * virStreamPtr st = virStreamNew(conn, 0); + * int fd = open("demo.iso", O_WRONLY, 0600) + * + * virConnectDownloadFile(conn, "demo.iso", st); + * + * while (1) { + * char buf[1024]; + * int got = virStreamRecv(st, buf, 1024); + * if (got < 0) + * break; + * if (got == 0) { + * virStreamFinish(st); + * break; + * } + * int offset = 0; + * while (offset < got) { + * int sent = write(fd, buf+offset, got-offset) + * if (sent < 0) { + * virStreamAbort(st); + * goto done; + * } + * offset += sent; + * } + * } + * if (virStreamFinish(st) < 0) + * ... report an error .... + * done: + * virStreamFree(st); + * close(fd); + * + * + * Returns the number of bytes read, which may be less + * than requested. + * + * Returns 0 when the end of the stream is reached, at + * which time the caller should invoke virStreamFinish() + * to get confirmation of stream completion. + * + * Returns -1 upon error, at which time the stream will + * be marked as aborted, and the caller should now release + * the stream with virStreamFree. + * + * Returns -2 if there is no data pending to be read & the + * stream is marked as non-blocking. + */ +int virStreamRecv(virStreamPtr stream, + char *data, + size_t nbytes) +{ + DEBUG("stream=%p, data=%p, nbytes=%zi", stream, data, nbytes); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_STREAM(stream)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if (stream->driver && + stream->driver->streamRecv) { + int ret; + ret = (stream->driver->streamRecv)(stream, data, nbytes); + if (ret == -2) + return -2; + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(stream->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(stream->conn); + return -1; +} + + +/** + * virStreamSendAll: + * @stream: pointer to the stream object + * @handler: source callback for reading data from application + * @opaque: application defined data + * + * Send the entire data stream, reading the data from the + * requested data source. This is simply a convenient alternative + * to virStreamSend, for apps that do blocking-I/o. + * + * A example using this with a hypothetical file upload + * API looks like + * + * int mysource(virStreamPtr st, char *buf, int nbytes, void *opaque) { + * int *fd = opaque; + * + * return read(*fd, buf, nbytes); + * } + * + * virStreamPtr st = virStreamNew(conn, 0); + * int fd = open("demo.iso", O_RDONLY) + * + * virConnectUploadFile(conn, st); + * if (virStreamSendAll(st, mysource, &fd) < 0) { + * ...report an error ... + * goto done; + * } + * if (virStreamFinish(st) < 0) + * ...report an error... + * virStreamFree(st); + * close(fd); + * + * Returns 0 if all the data was succesfully sent. The caller + * should invoke virStreamFinish(st) to flush the stream upon + * success and then virStreamFree + * + * Returns -1 upon any error, with virStreamAbort() already + * having been called, so the caller need only call + * virStreamFree() + */ +int virStreamSendAll(virStreamPtr stream, + virStreamSourceFunc handler, + void *opaque) +{ + char *bytes = NULL; + int want = 1024*64; + int ret = -1; + DEBUG("stream=%p, handler=%p, opaque=%p", stream, handler, opaque); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_STREAM(stream)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if (stream->flags & VIR_STREAM_NONBLOCK) { + virLibConnError(NULL, VIR_ERR_OPERATION_INVALID, + _("data sources cannot be used for non-blocking streams")); + goto cleanup; + } + + if (VIR_ALLOC_N(bytes, want) < 0) { + virReportOOMError(stream->conn); + goto cleanup; + } + + for (;;) { + int got, offset = 0; + got = (handler)(stream, bytes, want, opaque); + if (got < 0) { + virStreamAbort(stream); + goto cleanup; + } + if (got == 0) + break; + while (offset < got) { + int done; + done = virStreamSend(stream, bytes + offset, got - offset); + if (done < 0) + goto cleanup; + offset += done; + } + } + ret = 0; + +cleanup: + VIR_FREE(bytes); + + /* Copy to connection error object for back compatability */ + if (ret != 0) + virSetConnError(stream->conn); + + return ret; +} + + +/** + * virStreamRecvAll: + * @stream: pointer to the stream object + * @handler: sink callback for writing data to application + * @opaque: application defined data + * + * Receive the entire data stream, sending the data to the + * requested data sink. This is simply a convenient alternative + * to virStreamRecv, for apps that do blocking-I/o. + * + * A example using this with a hypothetical file download + * API looks like + * + * int mysink(virStreamPtr st, const char *buf, int nbytes, void *opaque) { + * int *fd = opaque; + * + * return write(*fd, buf, nbytes); + * } + * + * virStreamPtr st = virStreamNew(conn, 0); + * int fd = open("demo.iso", O_WRONLY) + * + * virConnectUploadFile(conn, st); + * if (virStreamRecvAll(st, mysink, &fd) < 0) { + * ...report an error ... + * goto done; + * } + * if (virStreamFinish(st) < 0) + * ...report an error... + * virStreamFree(st); + * close(fd); + * + * Returns 0 if all the data was succesfully received. The caller + * should invoke virStreamFinish(st) to flush the stream upon + * success and then virStreamFree + * + * Returns -1 upon any error, with virStreamAbort() already + * having been called, so the caller need only call + * virStreamFree() + */ +int virStreamRecvAll(virStreamPtr stream, + virStreamSinkFunc handler, + void *opaque) +{ + char *bytes = NULL; + int want = 1024*64; + int ret = -1; + DEBUG("stream=%p, handler=%p, opaque=%p", stream, handler, opaque); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_STREAM(stream)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if (stream->flags & VIR_STREAM_NONBLOCK) { + virLibConnError(NULL, VIR_ERR_OPERATION_INVALID, + _("data sinks cannot be used for non-blocking streams")); + goto cleanup; + } + + + if (VIR_ALLOC_N(bytes, want) < 0) { + virReportOOMError(stream->conn); + goto cleanup; + } + + for (;;) { + int got, offset = 0; + got = virStreamRecv(stream, bytes, want); + if (got < 0) + goto cleanup; + if (got == 0) + break; + while (offset < got) { + int done; + done = (handler)(stream, bytes + offset, got - offset, opaque); + if (done < 0) { + virStreamAbort(stream); + goto cleanup; + } + offset += done; + } + } + ret = 0; + +cleanup: + VIR_FREE(bytes); + + /* Copy to connection error object for back compatability */ + if (ret != 0) + virSetConnError(stream->conn); + + return ret; +} + + +/** + * virStreamEventAddCallback + * @stream: pointer to the stream object + * @events: set of events to monitor + * @cb: callback to invoke when an event occurs + * @opaque: application defined data + * @ff: callback to free @opaque data + * + * Register a callback to be notified when a stream + * becomes writable, or readable. This is most commonly + * used in conjunction with non-blocking data streams + * to integrate into an event loop + * + * Returns 0 on success, -1 upon error + */ +int virStreamEventAddCallback(virStreamPtr stream, + int events, + virStreamEventCallback cb, + void *opaque, + virFreeCallback ff) +{ + DEBUG("stream=%p, events=%d, cb=%p, opaque=%p, ff=%p", stream, events, cb, opaque, ff); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_STREAM(stream)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if (stream->driver && + stream->driver->streamAddCallback) { + int ret; + ret = (stream->driver->streamAddCallback)(stream, events, cb, opaque, ff); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(stream->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(stream->conn); + return -1; +} + + +/** + * virStreamEventUpdateCallback + * @stream: pointer to the stream object + * @events: set of events to monitor + * + * Changes the set of events to monitor for a stream. This allows + * for event notification to be changed without having to + * unregister & register the callback completely. This method + * is guarenteed to succeed if a callback is already registered + * + * Returns 0 on success, -1 if no callback is registered + */ +int virStreamEventUpdateCallback(virStreamPtr stream, + int events) +{ + DEBUG("stream=%p, events=%d", stream, events); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_STREAM(stream)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if (stream->driver && + stream->driver->streamUpdateCallback) { + int ret; + ret = (stream->driver->streamUpdateCallback)(stream, events); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (stream->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(stream->conn); + return -1; +} + +/** + * virStreamEventRemoveCallback + * @stream: pointer to the stream object + * + * Remove a event callback from the stream + * + * Returns 0 on success, -1 on error + */ +int virStreamEventRemoveCallback(virStreamPtr stream) +{ + DEBUG("stream=%p", stream); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_STREAM(stream)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if (stream->driver && + stream->driver->streamRemoveCallback) { + int ret; + ret = (stream->driver->streamRemoveCallback)(stream); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (stream->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(stream->conn); + return -1; +} + +/** + * virStreamFinish: + * @stream: pointer to the stream object + * + * Indicate that there is no further data is to be transmitted + * on the stream. For output streams this should be called once + * all data has been written. For input streams this should be + * called once virStreamRecv returns end-of-file. + * + * This method is a synchronization point for all asynchronous + * errors, so if this returns a success code the application can + * be sure that all data has been successfully processed. + * + * Returns 0 on success, -1 upon error + */ +int virStreamFinish(virStreamPtr stream) +{ + DEBUG("stream=%p", stream); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_STREAM(stream)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if (stream->driver && + stream->driver->streamFinish) { + int ret; + ret = (stream->driver->streamFinish)(stream); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (stream->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(stream->conn); + return -1; +} + +/** + * virStreamAbort: + * @stream: pointer to the stream object + * + * Request that the in progress data transfer be cancelled + * abnormally before the end of the stream has been reached. + * For output streams this can be used to inform the driver + * that the stream is being terminated early. For input + * streams this can be used to inform the driver that it + * should stop sending data. + * + * Returns 0 on success, -1 upon error + */ +int virStreamAbort(virStreamPtr stream) +{ + DEBUG("stream=%p", stream); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_STREAM(stream)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + if (stream->driver && + stream->driver->streamAbort) { + int ret; + ret = (stream->driver->streamAbort)(stream); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (stream->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(stream->conn); + return -1; +} + +/** + * virStreamFree: + * @stream: pointer to the stream object + * + * Decrement the reference count on a stream, releasing + * the stream object if the reference count has hit zero. + * + * There must not be a active data transfer in progress + * when releasing the stream. If a stream needs to be + * disposed of prior to end of stream being reached, then + * the virStreamAbort function should be called first. + * + * Returns 0 upon success, or -1 on error + */ +int virStreamFree(virStreamPtr stream) +{ + DEBUG("stream=%p", stream); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_STREAM(stream)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + return (-1); + } + + /* XXX Enforce shutdown before free'ing resources ? */ + + if (virUnrefStream(stream) < 0) + return (-1); + return (0); +} diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index f8598c7649..ad579d920a 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -78,6 +78,8 @@ virGetNodeDevice; virUnrefDomain; virUnrefConnect; virUnrefSecret; +virGetStream; +virUnrefStream; # domain_conf.h diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index cff50d5d17..7226e88a0d 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -312,4 +312,20 @@ LIBVIRT_0.7.1 { virSecretFree; } LIBVIRT_0.7.0; +LIBVIRT_0.7.2 { + global: + virStreamNew; + virStreamRef; + virStreamSend; + virStreamRecv; + virStreamSendAll; + virStreamRecvAll; + virStreamEventAddCallback; + virStreamEventUpdateCallback; + virStreamEventRemoveCallback; + virStreamFinish; + virStreamAbort; + virStreamFree; +} LIBVIRT_0.7.1; + # .... define new API here using predicted next version number ....