summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--mscp/__init__.py1
-rw-r--r--mscp/mscp.py120
-rw-r--r--setup.py12
-rw-r--r--src/pymscp.c189
5 files changed, 291 insertions, 35 deletions
diff --git a/.gitignore b/.gitignore
index 8a85fbf..0723882 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,7 @@ html
compile_commands.json
CMakeUserPresets.json
.*.swp
+
+dist
+*.egg-info
+__pycache__
diff --git a/mscp/__init__.py b/mscp/__init__.py
new file mode 100644
index 0000000..64b226b
--- /dev/null
+++ b/mscp/__init__.py
@@ -0,0 +1 @@
+from mscp.mscp import *
diff --git a/mscp/mscp.py b/mscp/mscp.py
new file mode 100644
index 0000000..6a18c0f
--- /dev/null
+++ b/mscp/mscp.py
@@ -0,0 +1,120 @@
+
+import pymscp
+
+_STATE_INIT = 0
+_STATE_CONNECTED = 1
+_STATE_PREPARED = 2
+_STATE_RUNNING = 3
+_STATE_STOPPED = 4
+_STATE_JOINED = 5
+_STATE_CLEANED = 6
+_STATE_RELEASED = 7
+
+_state_str = {
+ _STATE_INIT: "init",
+ _STATE_CONNECTED: "connected",
+ _STATE_PREPARED: "prepared",
+ _STATE_RUNNING: "running",
+ _STATE_STOPPED: "stopped",
+ _STATE_JOINED: "joined",
+ _STATE_CLEANED: "cleaned",
+ _STATE_RELEASED: "released",
+}
+
+
+class mscp:
+ def __init__(self, remote: str, direction: int, **kwargs):
+ """
+ See src/pymscp.c:wrap_mscp_init() to determine keyword arguments.
+ """
+ kwargs["remote"] = remote
+ kwargs["direction"] = direction
+ self.m = pymscp.mscp_init(**kwargs)
+
+ self.src_paths = []
+ self.dst_path = None
+ self.state = _STATE_INIT
+
+ def _state2str(self):
+ return _state_str[self.state]
+
+
+ def connect(self):
+ if not (self.state == _STATE_INIT or state.state == _STATE_CLEANED):
+ raise RuntimeError("invalid mscp state: {}".format(_state2str()))
+ pymscp.mscp_connect(m = self.m)
+ self.state = _CONNECTED
+
+ def add_src_path(self, src_path: str):
+ self.src_paths.append(src_path)
+ pymscp.mscp_add_src_path(m = self.m, src_path = src_path)
+
+ def set_dst_path(self, dst_path: str):
+ self.dst_path = dst_path
+ pymscp.mscp_set_dst_path(m = self.m, dst_path = dst_path);
+
+ def prepare(self):
+ if self.state != _STATE_CONNCTED:
+ raise RuntimeError("invalid mscp state: {}".format(_state2str()))
+ if not self.src_paths:
+ raise RuntimeError("src path list is empty")
+ if not self.dst_path:
+ raise RuntimeError("dst path is not set")
+
+ pymscp.mscp_prepare(m = self.m)
+ self.state = _STATE_PREPARED
+
+ def start(self):
+ if self.state != STATE_PREPARED:
+ raise RuntimeError("invalid mscp state: {}".format(_state2str()))
+
+ pymscp.mscp_start(m = self.m)
+ self.state = _STATE_RUNNING
+
+ def stop(self):
+ if self.state != _STATE_RUNNING:
+ raise RuntimeError("invalid mscp state: {}".format(_state2str()))
+ pymscp.mscp_stop(m = self.m)
+ self.state = _STATE_STOPPED
+
+ def join(self):
+ if not (self.state == STATE_RUNNING or self.state == _STATE_STOPPED):
+ raise RuntimeError("invalid mscp state: {}".format(_state2str()))
+ pymscp.mscp_join(m = self.m)
+ self.state = _STATE_JOINED
+
+ def stats(self):
+ return pymscp.mscp_get_stats(m = self.m)
+
+ def cleanup(self):
+ if self.state != _STATE_JOIND:
+ raise RuntimeError("invalid mscp state: {}".format(_state2str()))
+ pymscp.mscp_cleanup()
+ self.state = _STATE_CLEAND
+
+ def free(self):
+ if self.state != _STATE_CLEANED:
+ raise RuntimeError("invalid mscp state: {}".format(_state2str()))
+ pymscp.mscp_free()
+
+
+ # Simple interface: mscp.copy(src, dst)
+ def copy(self, src, dst, nonblock = False):
+ if self.state < _STATE_CONNECTED:
+ self.connect()
+
+ if type(src) == list:
+ self.src_paths += src
+ elif type(src) == str:
+ self.src_paths.append(src)
+ else:
+ raise ValueError("src must be str of list: '{}'".format(src))
+
+ self.dst_path = dst
+
+ self.prepare()
+ self.start()
+ if nonblock:
+ return
+
+ self.cleanup()
diff --git a/setup.py b/setup.py
index dae605b..34899d5 100644
--- a/setup.py
+++ b/setup.py
@@ -1,7 +1,15 @@
-from distutils.core import setup, Extension
+from setuptools import setup, Extension, find_packages
setup(
- name='pymscp',
+ name='mscp',
+ version = "0.0.1",
+ description = "libmscp python binding",
+ author = "Ryo Nakamura",
+ author_email = "upa@haeena.net",
+ url = "https://github.com/upa/mscp",
+ packages = find_packages("mscp"),
+ package_dir = {"": "mscp"},
+ py_modules = [ "mscp" ],
ext_modules = [
Extension(
'pymscp',
diff --git a/src/pymscp.c b/src/pymscp.c
index 201ce87..ef7e389 100644
--- a/src/pymscp.c
+++ b/src/pymscp.c
@@ -2,6 +2,57 @@
#include <Python.h>
#include <mscp.h>
+/*
+ * This is a wrapper for python binding of libmscp. setup.py builds
+ * pymscp.c after libmscp was built, and setup.py installs pymscp
+ * modlue and mscp python module (mscp/mscp.py), which is a warpper
+ * for pymscp.
+ */
+
+#define MAX_MSCP_INSTS 16
+
+/* XXX: cut corners */
+struct mscp *insts[MAX_MSCP_INSTS];
+
+static int add_mscp_inst(struct mscp *m)
+{
+ int n;
+ for (n = 0; n < MAX_MSCP_INSTS; n++) {
+ if (insts[n] == NULL) {
+ insts[n] = m;
+ return 0;
+ }
+ }
+
+ return -1; /* full of mscp instances */
+}
+
+static struct mscp *get_mscp_inst(unsigned long long maddr)
+{
+ int n;
+ for (n = 0; n < MAX_MSCP_INSTS; n++) {
+ if (insts[n] == (void *)maddr)
+ return insts[n];
+ }
+
+ return NULL;
+}
+
+static int release_mscp_inst(struct mscp *m)
+{
+ int n;
+ for (n = 0; n < MAX_MSCP_INSTS; n++) {
+ if (insts[n] == m) {
+ insts[n] = NULL;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+
+/* wrapper functions */
+
static PyObject *wrap_mscp_init(PyObject *sef, PyObject *args, PyObject *kw)
{
/*
@@ -11,8 +62,10 @@ static PyObject *wrap_mscp_init(PyObject *sef, PyObject *args, PyObject *kw)
char *remote;
char *keywords[] = {
- /* mscp_opts */
+ "remote", /* const char * */
"direction", /* int, MSCP_DIRECTION_L2R or MSCP_DIRECTION_R2L */
+
+ /* mscp_opts */
"nr_threads", /* int */
"nr_ahead", /* int */
"min_chunk_sz", /* unsigned long */
@@ -36,14 +89,16 @@ static PyObject *wrap_mscp_init(PyObject *sef, PyObject *args, PyObject *kw)
"enable_nagle", /* bool */
NULL,
};
- const char *fmt = "siiikkkzii" "zzzzzzzzipp";
- char *coremask;
- char *login_name, *port, *identity, *cipher, *hmac, *compress;
- char *password, *passphrase;
+ const char *fmt = "si" "|iikkkzii" "zzzzzzzzipp";
+ char *coremask = NULL;
+ char *login_name = NULL, *port = NULL, *identity = NULL;
+ char *cipher = NULL, *hmac = NULL, *compress = NULL;
+ char *password = NULL, *passphrase = NULL;
- struct mscp_opts mo;
struct mscp_ssh_opts so;
+ struct mscp_opts mo;
struct mscp *m;
+ int direction;
int ret;
memset(&mo, 0, sizeof(mo));
@@ -51,7 +106,7 @@ static PyObject *wrap_mscp_init(PyObject *sef, PyObject *args, PyObject *kw)
ret = PyArg_ParseTupleAndKeywords(args, kw, fmt, keywords,
&remote,
- &mo.direction,
+ &direction,
&mo.nr_threads,
&mo.nr_ahead,
&mo.min_chunk_sz,
@@ -68,6 +123,7 @@ static PyObject *wrap_mscp_init(PyObject *sef, PyObject *args, PyObject *kw)
&compress,
&password,
&passphrase,
+ &so.debug_level,
&so.no_hostkey_check,
&so.enable_nagle);
@@ -75,29 +131,35 @@ static PyObject *wrap_mscp_init(PyObject *sef, PyObject *args, PyObject *kw)
return NULL;
if (coremask)
- strncpy(mo.coremask, coremask, MSCP_MAX_COREMASK_STR);
+ strncpy(mo.coremask, coremask, MSCP_MAX_COREMASK_STR - 1);
if (login_name)
- strncpy(so.login_name, login_name, MSCP_SSH_MAX_LOGIN_NAME);
+ strncpy(so.login_name, login_name, MSCP_SSH_MAX_LOGIN_NAME - 1);
if (port)
- strncpy(so.port, port, MSCP_SSH_MAX_PORT_STR);
+ strncpy(so.port, port, MSCP_SSH_MAX_PORT_STR - 1);
if (identity)
- strncpy(so.identity, identity, MSCP_SSH_MAX_IDENTITY_PATH);
+ strncpy(so.identity, identity, MSCP_SSH_MAX_IDENTITY_PATH - 1);
if (cipher)
- strncpy(so.cipher, cipher, MSCP_SSH_MAX_CIPHER_STR);
+ strncpy(so.cipher, cipher, MSCP_SSH_MAX_CIPHER_STR - 1);
if (hmac)
- strncpy(so.hmac, hmac, MSCP_SSH_MAX_HMAC_STR);
+ strncpy(so.hmac, hmac, MSCP_SSH_MAX_HMAC_STR - 1);
if (compress)
- strncpy(so.compress, compress, MSCP_SSH_MAX_COMP_STR);
+ strncpy(so.compress, compress, MSCP_SSH_MAX_COMP_STR - 1);
if (password)
- strncpy(so.password, password, MSCP_SSH_MAX_PASSWORD);
+ strncpy(so.password, password, MSCP_SSH_MAX_PASSWORD - 1);
if (passphrase)
- strncpy(so.passphrase, passphrase, MSCP_SSH_MAX_PASSPHRASE);
+ strncpy(so.passphrase, passphrase, MSCP_SSH_MAX_PASSPHRASE - 1);
- m = mscp_init(remote, &mo, &so);
+ m = mscp_init(remote, direction, &mo, &so);
if (!m)
return NULL;
+ if (add_mscp_inst(m) < 0) {
+ PyErr_Format(PyExc_RuntimeError, "too many mscp isntances");
+ mscp_free(m);
+ return NULL;
+ }
+
return Py_BuildValue("K", (unsigned long long)m);
}
@@ -110,7 +172,12 @@ static PyObject *wrap_mscp_connect(PyObject *self, PyObject *args, PyObject *kw)
if (!PyArg_ParseTupleAndKeywords(args, kw, "K", keywords, &maddr))
return NULL;
- m = (void *)maddr;
+ m = get_mscp_inst(maddr);
+ if (!m) {
+ PyErr_Format(PyExc_RuntimeError, "invalid mscp address");
+ return NULL;
+ }
+
if (mscp_connect(m) < 0) {
PyErr_Format(PyExc_RuntimeError, mscp_get_error());
return NULL;
@@ -129,7 +196,12 @@ static PyObject *wrap_mscp_add_src_path(PyObject *self, PyObject *args, PyObject
if (!PyArg_ParseTupleAndKeywords(args, kw, "Ks", keywords, &maddr, &src_path))
return NULL;
- m = (void *)maddr;
+ m = get_mscp_inst(maddr);
+ if (!m) {
+ PyErr_Format(PyExc_RuntimeError, "invalid mscp address");
+ return NULL;
+ }
+
if (mscp_add_src_path(m, src_path) < 0) {
PyErr_Format(PyExc_RuntimeError, mscp_get_error());
return NULL;
@@ -148,7 +220,12 @@ static PyObject *wrap_mscp_set_dst_path(PyObject *self, PyObject *args, PyObject
if (!PyArg_ParseTupleAndKeywords(args, kw, "Ks", keywords, &maddr, &dst_path))
return NULL;
- m = (void *)maddr;
+ m = get_mscp_inst(maddr);
+ if (!m) {
+ PyErr_Format(PyExc_RuntimeError, "invalid mscp address");
+ return NULL;
+ }
+
if (mscp_set_dst_path(m, dst_path) < 0) {
PyErr_Format(PyExc_RuntimeError, mscp_get_error());
return NULL;
@@ -166,7 +243,12 @@ static PyObject *wrap_mscp_prepare(PyObject *self, PyObject *args, PyObject *kw)
if (!PyArg_ParseTupleAndKeywords(args, kw, "K", keywords, &maddr))
return NULL;
- m = (void *)maddr;
+ m = get_mscp_inst(maddr);
+ if (!m) {
+ PyErr_Format(PyExc_RuntimeError, "invalid mscp address");
+ return NULL;
+ }
+
if (mscp_prepare(m) < 0) {
PyErr_Format(PyExc_RuntimeError, mscp_get_error());
return NULL;
@@ -184,7 +266,12 @@ static PyObject *wrap_mscp_start(PyObject *self, PyObject *args, PyObject *kw)
if (!PyArg_ParseTupleAndKeywords(args, kw, "K", keywords, &maddr))
return NULL;
- m = (void *)maddr;
+ m = get_mscp_inst(maddr);
+ if (!m) {
+ PyErr_Format(PyExc_RuntimeError, "invalid mscp address");
+ return NULL;
+ }
+
if (mscp_start(m) < 0) {
PyErr_Format(PyExc_RuntimeError, mscp_get_error());
return NULL;
@@ -202,7 +289,12 @@ static PyObject *wrap_mscp_stop(PyObject *self, PyObject *args, PyObject *kw)
if (!PyArg_ParseTupleAndKeywords(args, kw, "K", keywords, &maddr))
return NULL;
- m = (void *)maddr;
+ m = get_mscp_inst(maddr);
+ if (!m) {
+ PyErr_Format(PyExc_RuntimeError, "invalid mscp address");
+ return NULL;
+ }
+
mscp_stop(m);
return Py_BuildValue("");
@@ -217,7 +309,12 @@ static PyObject *wrap_mscp_join(PyObject *self, PyObject *args, PyObject *kw)
if (!PyArg_ParseTupleAndKeywords(args, kw, "K", keywords, &maddr))
return NULL;
- m = (void *)maddr;
+ m = get_mscp_inst(maddr);
+ if (!m) {
+ PyErr_Format(PyExc_RuntimeError, "invalid mscp address");
+ return NULL;
+ }
+
if (mscp_join(m) < 0) {
PyErr_Format(PyExc_RuntimeError, mscp_get_error());
return NULL;
@@ -236,7 +333,12 @@ static PyObject *wrap_mscp_get_stats(PyObject *self, PyObject *args, PyObject *k
if (!PyArg_ParseTupleAndKeywords(args, kw, "K", keywords, &maddr))
return NULL;
- m = (void *)maddr;
+ m = get_mscp_inst(maddr);
+ if (!m) {
+ PyErr_Format(PyExc_RuntimeError, "invalid mscp address");
+ return NULL;
+ }
+
mscp_get_stats(m, &s);
return Py_BuildValue("KKd", s.total, s.done, s.finished);
@@ -251,7 +353,12 @@ static PyObject *wrap_mscp_cleanup(PyObject *self, PyObject *args, PyObject *kw)
if (!PyArg_ParseTupleAndKeywords(args, kw, "K", keywords, &maddr))
return NULL;
- m = (void *)maddr;
+ m = get_mscp_inst(maddr);
+ if (!m) {
+ PyErr_Format(PyExc_RuntimeError, "invalid mscp address");
+ return NULL;
+ }
+
mscp_cleanup(m);
return Py_BuildValue("");
@@ -266,13 +373,19 @@ static PyObject *wrap_mscp_free(PyObject *self, PyObject *args, PyObject *kw)
if (!PyArg_ParseTupleAndKeywords(args, kw, "K", keywords, &maddr))
return NULL;
- m = (void *)maddr;
+ m = get_mscp_inst(maddr);
+ if (!m) {
+ PyErr_Format(PyExc_RuntimeError, "invalid mscp address");
+ return NULL;
+ }
+
+ release_mscp_inst(m);
mscp_free(m);
return Py_BuildValue("");
}
-static PyMethodDef PyMscpMethods[] = {
+static PyMethodDef pymscpMethods[] = {
{
"mscp_init", (PyCFunction)wrap_mscp_init,
METH_VARARGS | METH_KEYWORDS, NULL
@@ -317,15 +430,25 @@ static PyMethodDef PyMscpMethods[] = {
"mscp_free", (PyCFunction)wrap_mscp_free,
METH_VARARGS | METH_KEYWORDS, NULL
},
-
{ NULL, NULL, 0, NULL },
};
-static PyModuleDef PyMscpModule = {
- PyModuleDef_HEAD_INIT, "PyMscp", NULL, -1, PyMscpMethods,
+static PyModuleDef pymscpModule = {
+ PyModuleDef_HEAD_INIT, "pymscp", NULL, -1, pymscpMethods,
};
-PyMODINIT_FUNC PyInit_PyMscp(void) {
- return PyModule_Create(&PyMscpModule);
+PyMODINIT_FUNC PyInit_pymscp(void) {
+ PyObject *mod = PyModule_Create(&pymscpModule);
+
+ PyModule_AddIntConstant(mod, "LOCAL2REMOTE", MSCP_DIRECTION_L2R);
+ PyModule_AddIntConstant(mod, "REMOTE2LOCAL", MSCP_DIRECTION_R2L);
+ PyModule_AddIntConstant(mod, "SEVERITY_NONE", MSCP_SEVERITY_NONE);
+ PyModule_AddIntConstant(mod, "SEVERITY_ERR", MSCP_SEVERITY_ERR);
+ PyModule_AddIntConstant(mod, "SEVERITY_WARN", MSCP_SEVERITY_WARN);
+ PyModule_AddIntConstant(mod, "SEVERITY_NOTICE", MSCP_SEVERITY_NOTICE);
+ PyModule_AddIntConstant(mod, "SEVERITY_INFO", MSCP_SEVERITY_INFO);
+ PyModule_AddIntConstant(mod, "SEVERITY_DEBUG", MSCP_SEVERITY_DEBUG);
+
+ return mod;
}