diff options
Diffstat (limited to '')
-rw-r--r-- | lib/smbconf/pysmbconf.c | 813 | ||||
-rw-r--r-- | lib/smbconf/pysmbconf.h | 36 | ||||
-rw-r--r-- | lib/smbconf/smbconf.c | 511 | ||||
-rw-r--r-- | lib/smbconf/smbconf.h | 505 | ||||
-rw-r--r-- | lib/smbconf/smbconf_private.h | 96 | ||||
-rw-r--r-- | lib/smbconf/smbconf_txt.c | 682 | ||||
-rw-r--r-- | lib/smbconf/smbconf_txt.h | 33 | ||||
-rw-r--r-- | lib/smbconf/smbconf_util.c | 151 | ||||
-rw-r--r-- | lib/smbconf/wscript_build | 10 |
9 files changed, 2837 insertions, 0 deletions
diff --git a/lib/smbconf/pysmbconf.c b/lib/smbconf/pysmbconf.c new file mode 100644 index 0000000..2538127 --- /dev/null +++ b/lib/smbconf/pysmbconf.c @@ -0,0 +1,813 @@ +/* + * Unix SMB/CIFS implementation. + * libsmbconf - Samba configuration library - Python bindings + * + * Copyright (C) John Mulligan <phlogistonjohn@asynchrono.us> 2022 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "lib/replace/system/python.h" +#include "includes.h" +#include "python/py3compat.h" + +#include "lib/smbconf/smbconf.h" +#include "lib/smbconf/smbconf_txt.h" +#include "lib/smbconf/pysmbconf.h" + +static PyObject *PyExc_SMBConfError; + +static void py_raise_SMBConfError(sbcErr err) +{ + PyObject *v = NULL; + PyObject *args = NULL; + + /* + * TODO: have the exception type accept arguments in new/init + */ + args = Py_BuildValue("(is)", err, sbcErrorString(err)); + if (args == NULL) { + PyErr_Format(PyExc_SMBConfError, "[%d]: %s", err, + sbcErrorString(err)); + return; + } + v = PyObject_Call(PyExc_SMBConfError, args, NULL); + if (v == NULL) { + Py_CLEAR(args); + return; + } + /* + * It's clearer to set an explicit error_code attribute for use in calling + * code to check what kind of SMBConfError was raised. + */ + if (PyObject_SetAttrString(v, "error_code", PyTuple_GetItem(args, 0)) == -1) { + Py_CLEAR(v); + Py_CLEAR(args); + return; + } + Py_CLEAR(args); + PyErr_SetObject((PyObject *) Py_TYPE(v), v); + Py_DECREF(v); +} + +/* + * py_from_smbconf_service returns a python tuple that is basically equivalent + * to the struct smbconf_service type content-wise. + */ +static PyObject *py_from_smbconf_service(struct smbconf_service *svc) +{ + uint32_t count; + PyObject *plist = PyList_New(svc->num_params); + if (plist == NULL) { + return NULL; + } + + for (count = 0; count < svc->num_params; count++) { + PyObject *pt = Py_BuildValue("(ss)", + svc->param_names[count], + svc->param_values[count]); + if (pt == NULL) { + Py_CLEAR(plist); + return NULL; + } + if (PyList_SetItem(plist, count, pt) < 0) { + Py_CLEAR(pt); + Py_CLEAR(plist); + return NULL; + } + } + return Py_BuildValue("(sO)", svc->name, plist); +} + +static PyObject *obj_new(PyTypeObject * type, PyObject * args, PyObject * kwds) +{ + py_SMBConf_Object *self = (py_SMBConf_Object *) type->tp_alloc(type, 0); + if (self == NULL) { + return NULL; + } + + self->mem_ctx = talloc_new(NULL); + if (self->mem_ctx == NULL) { + Py_DECREF(self); + return NULL; + } + + return (PyObject *) self; +} + +static void obj_dealloc(py_SMBConf_Object * self) +{ + if (self->conf_ctx != NULL) { + smbconf_shutdown(self->conf_ctx); + } + talloc_free(self->mem_ctx); + Py_TYPE(self)->tp_free((PyObject *) self); +} + +static bool obj_ready(py_SMBConf_Object * self) +{ + if (self->conf_ctx == NULL) { + PyErr_Format(PyExc_RuntimeError, + "attempt to use an uninitialized SMBConf object"); + return false; + } + return true; +} + +static PyObject *obj_requires_messaging(py_SMBConf_Object * self, + PyObject * Py_UNUSED(ignored)) +{ + if (!obj_ready(self)) { + return NULL; + } + if (smbconf_backend_requires_messaging(self->conf_ctx)) { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +} + +static PyObject *obj_is_writable(py_SMBConf_Object * self, + PyObject * Py_UNUSED(ignored)) +{ + if (!obj_ready(self)) { + return NULL; + } + if (smbconf_is_writeable(self->conf_ctx)) { + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +} + +static PyObject *obj_share_names(py_SMBConf_Object * self, + PyObject * Py_UNUSED(ignored)) +{ + sbcErr err; + uint32_t count; + uint32_t num_shares; + char **share_names = NULL; + PyObject *slist = NULL; + TALLOC_CTX *mem_ctx = NULL; + + if (!obj_ready(self)) { + return NULL; + } + + mem_ctx = talloc_new(self->mem_ctx); + if (mem_ctx == NULL) { + PyErr_NoMemory(); + return NULL; + } + + err = + smbconf_get_share_names(self->conf_ctx, mem_ctx, &num_shares, + &share_names); + if (err != SBC_ERR_OK) { + talloc_free(mem_ctx); + py_raise_SMBConfError(err); + return NULL; + } + + slist = PyList_New(num_shares); + if (slist == NULL) { + talloc_free(mem_ctx); + return NULL; + } + for (count = 0; count < num_shares; count++) { + PyObject *ustr = PyUnicode_FromString(share_names[count]); + if (ustr == NULL) { + Py_CLEAR(slist); + talloc_free(mem_ctx); + return NULL; + } + if (PyList_SetItem(slist, count, ustr) < 0) { + Py_CLEAR(ustr); + Py_CLEAR(slist); + talloc_free(mem_ctx); + return NULL; + } + } + talloc_free(mem_ctx); + return slist; +} + +static PyObject *obj_get_share(py_SMBConf_Object * self, PyObject * args) +{ + sbcErr err; + char *servicename = NULL; + struct smbconf_service *svc = NULL; + PyObject *plist = NULL; + TALLOC_CTX *mem_ctx = NULL; + + if (!PyArg_ParseTuple(args, "s", &servicename)) { + return NULL; + } + + if (!obj_ready(self)) { + return NULL; + } + + mem_ctx = talloc_new(self->mem_ctx); + if (mem_ctx == NULL) { + PyErr_NoMemory(); + return NULL; + } + + err = smbconf_get_share(self->conf_ctx, mem_ctx, servicename, &svc); + if (err != SBC_ERR_OK) { + talloc_free(mem_ctx); + py_raise_SMBConfError(err); + return NULL; + } + /* + * if py_from_smbconf_service returns NULL, then an exception should + * already be set. No special error handling needed. + */ + plist = py_from_smbconf_service(svc); + talloc_free(mem_ctx); + return plist; +} + +static PyObject *obj_get_config(py_SMBConf_Object * self, + PyObject * Py_UNUSED(ignored)) +{ + sbcErr err; + PyObject *svclist = NULL; + TALLOC_CTX *mem_ctx = NULL; + uint32_t count; + uint32_t num_shares; + struct smbconf_service **svcs = NULL; + + if (!obj_ready(self)) { + return NULL; + } + + mem_ctx = talloc_new(self->mem_ctx); + if (mem_ctx == NULL) { + PyErr_NoMemory(); + return NULL; + } + + err = smbconf_get_config(self->conf_ctx, mem_ctx, &num_shares, &svcs); + if (err != SBC_ERR_OK) { + talloc_free(mem_ctx); + py_raise_SMBConfError(err); + return NULL; + } + + svclist = PyList_New(num_shares); + if (svclist == NULL) { + talloc_free(mem_ctx); + return NULL; + } + for (count = 0; count < num_shares; count++) { + PyObject *svcobj = py_from_smbconf_service(svcs[count]); + if (svcobj == NULL) { + Py_CLEAR(svclist); + talloc_free(mem_ctx); + return NULL; + } + if (PyList_SetItem(svclist, count, svcobj) < 0) { + Py_CLEAR(svcobj); + Py_CLEAR(svclist); + talloc_free(mem_ctx); + return NULL; + } + } + + talloc_free(mem_ctx); + return svclist; +} + +static PyObject *obj_create_share(py_SMBConf_Object * self, PyObject * args) +{ + sbcErr err; + char *servicename = NULL; + + if (!PyArg_ParseTuple(args, "s", &servicename)) { + return NULL; + } + + err = smbconf_create_share(self->conf_ctx, servicename); + if (err != SBC_ERR_OK) { + py_raise_SMBConfError(err); + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject *obj_drop(py_SMBConf_Object * self, + PyObject * Py_UNUSED(ignored)) +{ + sbcErr err; + + err = smbconf_drop(self->conf_ctx); + if (err != SBC_ERR_OK) { + py_raise_SMBConfError(err); + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject *obj_set_parameter(py_SMBConf_Object * self, PyObject * args) +{ + sbcErr err; + char *servicename = NULL; + char *param = NULL; + char *val = NULL; + + if (!PyArg_ParseTuple(args, "sss", &servicename, ¶m, &val)) { + return NULL; + } + + err = smbconf_set_parameter(self->conf_ctx, servicename, param, val); + if (err != SBC_ERR_OK) { + py_raise_SMBConfError(err); + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject *obj_set_global_parameter(py_SMBConf_Object * self, + PyObject * args) +{ + sbcErr err; + char *param = NULL; + char *val = NULL; + + if (!PyArg_ParseTuple(args, "ss", ¶m, &val)) { + return NULL; + } + + err = smbconf_set_global_parameter(self->conf_ctx, param, val); + if (err != SBC_ERR_OK) { + py_raise_SMBConfError(err); + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject *obj_delete_share(py_SMBConf_Object * self, PyObject * args) +{ + sbcErr err; + char *servicename = NULL; + + if (!PyArg_ParseTuple(args, "s", &servicename)) { + return NULL; + } + + err = smbconf_delete_share(self->conf_ctx, servicename); + if (err != SBC_ERR_OK) { + py_raise_SMBConfError(err); + return NULL; + } + Py_RETURN_NONE; +} + +static char *py_get_kv_str(TALLOC_CTX * mem_ctx, PyObject * obj, Py_ssize_t idx) +{ + char *ss = NULL; + PyObject *pystr = PySequence_GetItem(obj, idx); + if (pystr == NULL) { + return NULL; + } + if (!PyUnicode_Check(pystr)) { + PyErr_SetString(PyExc_TypeError, "keys/values expect a str"); + Py_CLEAR(pystr); + return NULL; + } + ss = talloc_strdup(mem_ctx, PyUnicode_AsUTF8(pystr)); + Py_CLEAR(pystr); + return ss; +} + +static PyObject *obj_create_set_share(py_SMBConf_Object * self, PyObject * args) +{ + sbcErr err; + char *servicename = NULL; + PyObject *kvs = NULL; + Py_ssize_t size, idx; + struct smbconf_service *tmp_service = NULL; + TALLOC_CTX *tmp_ctx = talloc_new(self->mem_ctx); + + if (!PyArg_ParseTuple(args, "sO", &servicename, &kvs)) { + talloc_free(tmp_ctx); + return NULL; + } + + if (PySequence_Check(kvs) == 0) { + PyErr_SetString(PyExc_TypeError, + "a sequence object is required"); + talloc_free(tmp_ctx); + return NULL; + } + + size = PySequence_Size(kvs); + if (size == -1) { + PyErr_SetString(PyExc_ValueError, "failed to get size"); + talloc_free(tmp_ctx); + return NULL; + } + + tmp_service = talloc_zero(tmp_ctx, struct smbconf_service); + if (tmp_service == NULL) { + PyErr_NoMemory(); + talloc_free(tmp_ctx); + return NULL; + } + + tmp_service->name = talloc_strdup(tmp_service, servicename); + if (tmp_service->name == NULL) { + PyErr_NoMemory(); + talloc_free(tmp_ctx); + return NULL; + } + tmp_service->num_params = (uint32_t) size; + tmp_service->param_names = talloc_array(tmp_ctx, char *, size); + if (tmp_service->param_names == NULL) { + PyErr_NoMemory(); + talloc_free(tmp_ctx); + return NULL; + } + tmp_service->param_values = talloc_array(tmp_ctx, char *, size); + if (tmp_service->param_values == NULL) { + PyErr_NoMemory(); + talloc_free(tmp_ctx); + return NULL; + } + + for (idx = 0; idx < size; idx++) { + char *tmp_str = NULL; + PyObject *tmp_pair = PySequence_GetItem(kvs, idx); + if (tmp_pair == NULL) { + talloc_free(tmp_ctx); + return NULL; + } + if (PySequence_Size(tmp_pair) != 2) { + PyErr_SetString(PyExc_ValueError, + "expecting two-item tuples"); + Py_CLEAR(tmp_pair); + talloc_free(tmp_ctx); + return NULL; + } + + /* fetch key */ + tmp_str = py_get_kv_str(tmp_ctx, tmp_pair, 0); + if (tmp_str == NULL) { + Py_CLEAR(tmp_pair); + talloc_free(tmp_ctx); + return NULL; + } + tmp_service->param_names[idx] = tmp_str; + + /* fetch value */ + tmp_str = py_get_kv_str(tmp_ctx, tmp_pair, 1); + if (tmp_str == NULL) { + Py_CLEAR(tmp_pair); + talloc_free(tmp_ctx); + return NULL; + } + tmp_service->param_values[idx] = tmp_str; + + Py_CLEAR(tmp_pair); + } + + err = smbconf_create_set_share(self->conf_ctx, tmp_service); + if (err != SBC_ERR_OK) { + py_raise_SMBConfError(err); + talloc_free(tmp_ctx); + return NULL; + } + talloc_free(tmp_ctx); + Py_RETURN_NONE; +} + +static PyObject *obj_delete_parameter(py_SMBConf_Object * self, PyObject * args) +{ + sbcErr err; + char *servicename = NULL; + char *param_name = NULL; + + if (!PyArg_ParseTuple(args, "ss", &servicename, ¶m_name)) { + return NULL; + } + + err = smbconf_delete_parameter(self->conf_ctx, servicename, param_name); + if (err != SBC_ERR_OK) { + py_raise_SMBConfError(err); + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject *obj_delete_global_parameter(py_SMBConf_Object * self, + PyObject * args) +{ + sbcErr err; + char *param_name = NULL; + + if (!PyArg_ParseTuple(args, "s", ¶m_name)) { + return NULL; + } + + err = smbconf_delete_global_parameter(self->conf_ctx, param_name); + if (err != SBC_ERR_OK) { + py_raise_SMBConfError(err); + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject *obj_transaction_start(py_SMBConf_Object * self, + PyObject * Py_UNUSED(ignored)) +{ + sbcErr err = smbconf_transaction_start(self->conf_ctx); + if (err != SBC_ERR_OK) { + py_raise_SMBConfError(err); + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject *obj_transaction_commit(py_SMBConf_Object * self, + PyObject * Py_UNUSED(ignored)) +{ + sbcErr err = smbconf_transaction_commit(self->conf_ctx); + if (err != SBC_ERR_OK) { + py_raise_SMBConfError(err); + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject *obj_transaction_cancel(py_SMBConf_Object * self, + PyObject * Py_UNUSED(ignored)) +{ + sbcErr err = smbconf_transaction_cancel(self->conf_ctx); + if (err != SBC_ERR_OK) { + py_raise_SMBConfError(err); + return NULL; + } + Py_RETURN_NONE; +} + +PyDoc_STRVAR(obj_requires_messaging_doc, +"requires_messaging() -> bool\n" +"\n" +"Returns true if the backend requires interprocess messaging.\n"); + +PyDoc_STRVAR(obj_is_writable_doc, +"is_writeable() -> bool\n" +"\n" +"Returns true if the SMBConf object's backend is writable.\n"); + +PyDoc_STRVAR(obj_share_names_doc, +"share_names() -> list[str]\n" +"\n" +"Return a list of the share names currently configured.\n" +"Includes the global section as a share name.\n"); + +PyDoc_STRVAR(obj_get_share_doc, +"get_share() -> (str, list[(str, str)])\n" +"\n" +"Given the name of a share, return a tuple of \n" +"(share_name, share_parms) where share_params is a list of\n" +"(param_name, param_value) tuples.\n" +"The term global can be specified to get global section parameters.\n"); + +PyDoc_STRVAR(obj_get_config_doc, +"get_config() -> list[(str, list[(str, str)])]\n" +"Return a list of tuples for every section/share of the current\n" +"configuration. Each tuple in the list is the same as described\n" +"for get_share().\n"); + +PyDoc_STRVAR(obj_create_share_doc, +"create_share(name: str) -> None\n" +"Create a new empty share in the configuration. The share\n" +"name must not exist or an error will be raised.\n"); + +PyDoc_STRVAR(obj_drop_doc, +"drop() -> None\n" +"Drop the entire configuration, resetting it to an empty state.\n"); + +PyDoc_STRVAR(obj_set_parameter_doc, +"set_parameter(str, str, str) -> None\n" +"Set a configuration parameter. Specify service name, parameter name,\n" +"and parameter value.\n"); + +PyDoc_STRVAR(obj_set_global_parameter_doc, +"set_global_parameter(str, str) -> None\n" +"Set a global configuration parameter. Specify the parameter name\n" +"and parameter value.\n"); + +PyDoc_STRVAR(obj_delete_share_doc, +"delete_share(str) -> None\n" +"Delete a service from the configuration.\n"); + +PyDoc_STRVAR(obj_create_set_share_doc, +"create_set_share(str, [(str, str)...]) -> None\n" +"Create and set the definition of a service.\n"); + +PyDoc_STRVAR(obj_delete_parameter_doc, +"delete_parameter(str, str) -> None\n" +"Delete a single configuration parameter.\n"); + +PyDoc_STRVAR(obj_delete_global_parameter_doc, +"delete_parameter(str, str) -> None\n" +"Delete a single global configuration parameter.\n"); + +PyDoc_STRVAR(obj_transaction_start_doc, +"transaction_start() -> None\n" +"Start a transaction.\n" +"Transactions allow making compound sets of changes atomically.\n"); + +PyDoc_STRVAR(obj_transaction_commit_doc, +"transaction_commit() -> None\n" +"Commit the transaction.\n"); + +PyDoc_STRVAR(obj_transaction_cancel_doc, +"transaction_cancel() -> None\n" +"Cancel the transaction.\n"); + +static PyMethodDef py_smbconf_obj_methods[] = { + { "requires_messaging", (PyCFunction) obj_requires_messaging, + METH_NOARGS, obj_requires_messaging_doc }, + { "is_writeable", (PyCFunction) obj_is_writable, METH_NOARGS, + obj_is_writable_doc }, + { "share_names", (PyCFunction) obj_share_names, METH_NOARGS, + obj_share_names_doc }, + { "get_share", (PyCFunction) obj_get_share, METH_VARARGS, + obj_get_share_doc }, + { "get_config", (PyCFunction) obj_get_config, METH_NOARGS, + obj_get_config_doc }, + { "create_share", (PyCFunction) obj_create_share, METH_VARARGS, + obj_create_share_doc }, + { "create_set_share", (PyCFunction) obj_create_set_share, METH_VARARGS, + obj_create_set_share_doc }, + { "drop", (PyCFunction) obj_drop, METH_NOARGS, + obj_drop_doc }, + { "set_parameter", (PyCFunction) obj_set_parameter, METH_VARARGS, + obj_set_parameter_doc }, + { "set_global_parameter", (PyCFunction) obj_set_global_parameter, + METH_VARARGS, obj_set_global_parameter_doc }, + { "delete_share", (PyCFunction) obj_delete_share, METH_VARARGS, + obj_delete_share_doc }, + { "delete_parameter", (PyCFunction) obj_delete_parameter, METH_VARARGS, + obj_delete_parameter_doc }, + { "delete_global_parameter", (PyCFunction) obj_delete_global_parameter, + METH_VARARGS, obj_delete_global_parameter_doc }, + { "transaction_start", (PyCFunction) obj_transaction_start, METH_NOARGS, + obj_transaction_start_doc }, + { "transaction_commit", (PyCFunction) obj_transaction_commit, + METH_NOARGS, obj_transaction_commit_doc }, + { "transaction_cancel", (PyCFunction) obj_transaction_cancel, + METH_NOARGS, obj_transaction_cancel_doc }, + { 0 }, +}; + +PyDoc_STRVAR(py_SMBConf_type_doc, +"SMBConf objects provide uniform access to Samba configuration backends.\n" +"\n" +"The SMBConf type should not be instantiated directly. Rather, use a\n" +"backend specific init function like init_txt.\n"); + +static PyTypeObject py_SMBConf_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "smbconf.SMBConf", + .tp_doc = py_SMBConf_type_doc, + .tp_basicsize = sizeof(py_SMBConf_Object), + .tp_methods = py_smbconf_obj_methods, + .tp_new = obj_new, + .tp_dealloc = (destructor) obj_dealloc, +}; + +static PyObject *py_init_txt(PyObject * module, PyObject * args) +{ + py_SMBConf_Object *obj; + sbcErr err; + char *path = NULL; + struct smbconf_ctx *conf_ctx = NULL; + + if (!PyArg_ParseTuple(args, "s", &path)) { + return NULL; + } + + obj = (py_SMBConf_Object *) obj_new(&py_SMBConf_Type, NULL, NULL); + if (obj == NULL) { + return NULL; + } + + err = smbconf_init_txt(obj->mem_ctx, &conf_ctx, path); + if (err != SBC_ERR_OK) { + Py_DECREF(obj); + py_raise_SMBConfError(err); + return NULL; + } + obj->conf_ctx = conf_ctx; + return (PyObject *) obj; +} + +static PyObject *py_smbconf_error(PyObject * module, PyObject * args) +{ + sbcErr errcode; + + if (!PyArg_ParseTuple(args, "i", &errcode)) { + return NULL; + } + + /* this always raises an exception. it doesn't return the exception. */ + py_raise_SMBConfError(errcode); + return NULL; +} + +static PyMethodDef pysmbconf_methods[] = { + { "init_txt", (PyCFunction) py_init_txt, METH_VARARGS, + "Return an SMBConf object for the given text config file." }, + { "_smbconf_error", (PyCFunction) py_smbconf_error, METH_VARARGS, + "Raise an SMBConfError based on the given error code." }, + { 0 }, +}; + +PyDoc_STRVAR(py_smbconf_doc, +"The smbconf module is a wrapper for Samba's smbconf library.\n" +"This library supports common functions to access the contents\n" +"of a configuration backend, such as the text-based smb.conf file\n" +"or the read-write registry backend.\n" +"The read-only functions on the SMBConf type function on both backend\n" +"types. Future, write based functions need a writable backend (registry).\n" +"\n" +"Note that the registry backend will be provided by a different\n" +"library module from the source3 tree (implementation TBD).\n"); + +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + .m_name = "smbconf", + .m_doc = py_smbconf_doc, + .m_size = -1, + .m_methods = pysmbconf_methods, +}; + +MODULE_INIT_FUNC(smbconf) +{ + PyObject *m = PyModule_Create(&moduledef); + if (m == NULL) { + return NULL; + } + + if (PyType_Ready(&py_SMBConf_Type) < 0) { + Py_DECREF(m); + return NULL; + } + Py_INCREF(&py_SMBConf_Type); + if (PyModule_AddObject(m, "SMBConf", (PyObject *) & py_SMBConf_Type) < + 0) { + Py_DECREF(&py_SMBConf_Type); + Py_DECREF(m); + return NULL; + } + + PyExc_SMBConfError = + PyErr_NewException(discard_const_p(char, "smbconf.SMBConfError"), + NULL, NULL); + if (PyExc_SMBConfError == NULL) { + Py_DECREF(m); + return NULL; + } + Py_INCREF(PyExc_SMBConfError); + if (PyModule_AddObject(m, "SMBConfError", PyExc_SMBConfError) < 0) { + Py_DECREF(PyExc_SMBConfError); + Py_DECREF(m); + return NULL; + } + +/* + * ADD_FLAGS macro borrowed from source3/libsmb/pylibsmb.c + */ +#define ADD_FLAGS(val) PyModule_AddObject(m, #val, PyLong_FromLong(val)) + + ADD_FLAGS(SBC_ERR_OK); + ADD_FLAGS(SBC_ERR_NOT_IMPLEMENTED); + ADD_FLAGS(SBC_ERR_NOT_SUPPORTED); + ADD_FLAGS(SBC_ERR_UNKNOWN_FAILURE); + ADD_FLAGS(SBC_ERR_NOMEM); + ADD_FLAGS(SBC_ERR_INVALID_PARAM); + ADD_FLAGS(SBC_ERR_BADFILE); + ADD_FLAGS(SBC_ERR_NO_SUCH_SERVICE); + ADD_FLAGS(SBC_ERR_IO_FAILURE); + ADD_FLAGS(SBC_ERR_CAN_NOT_COMPLETE); + ADD_FLAGS(SBC_ERR_NO_MORE_ITEMS); + ADD_FLAGS(SBC_ERR_FILE_EXISTS); + ADD_FLAGS(SBC_ERR_ACCESS_DENIED); + + return m; +} diff --git a/lib/smbconf/pysmbconf.h b/lib/smbconf/pysmbconf.h new file mode 100644 index 0000000..1e57bfb --- /dev/null +++ b/lib/smbconf/pysmbconf.h @@ -0,0 +1,36 @@ +/* + * Unix SMB/CIFS implementation. + * libsmbconf - Samba configuration library - Python bindings + * + * Copyright (C) John Mulligan <phlogistonjohn@asynchrono.us> 2022 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _PYSMBCONF_H_ +#define _PYSMBCONF_H_ + +#include "lib/replace/system/python.h" +#include "lib/smbconf/smbconf.h" + +typedef struct { + PyObject_HEAD + + /* C values embedded in our python type */ + TALLOC_CTX * mem_ctx; + struct smbconf_ctx *conf_ctx; +} py_SMBConf_Object; + + +#endif /* _PYSMBCONF_H_ */ diff --git a/lib/smbconf/smbconf.c b/lib/smbconf/smbconf.c new file mode 100644 index 0000000..4129ea5 --- /dev/null +++ b/lib/smbconf/smbconf.c @@ -0,0 +1,511 @@ +/* + * Unix SMB/CIFS implementation. + * libsmbconf - Samba configuration library + * Copyright (C) Michael Adam 2007-2008 + * Copyright (C) Guenther Deschner 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "smbconf_private.h" + +/********************************************************************** + * + * internal helper functions + * + **********************************************************************/ + +static sbcErr smbconf_global_check(struct smbconf_ctx *ctx) +{ + if (!smbconf_share_exists(ctx, GLOBAL_NAME)) { + return smbconf_create_share(ctx, GLOBAL_NAME); + } + + return SBC_ERR_OK; +} + + +/********************************************************************** + * + * The actual libsmbconf API functions that are exported. + * + **********************************************************************/ + +const char *sbcErrorString(sbcErr error) +{ + switch (error) { + case SBC_ERR_OK: + return "SBC_ERR_OK"; + case SBC_ERR_NOT_IMPLEMENTED: + return "SBC_ERR_NOT_IMPLEMENTED"; + case SBC_ERR_NOT_SUPPORTED: + return "SBC_ERR_NOT_SUPPORTED"; + case SBC_ERR_UNKNOWN_FAILURE: + return "SBC_ERR_UNKNOWN_FAILURE"; + case SBC_ERR_NOMEM: + return "SBC_ERR_NOMEM"; + case SBC_ERR_INVALID_PARAM: + return "SBC_ERR_INVALID_PARAM"; + case SBC_ERR_BADFILE: + return "SBC_ERR_BADFILE"; + case SBC_ERR_NO_SUCH_SERVICE: + return "SBC_ERR_NO_SUCH_SERVICE"; + case SBC_ERR_IO_FAILURE: + return "SBC_ERR_IO_FAILURE"; + case SBC_ERR_CAN_NOT_COMPLETE: + return "SBC_ERR_CAN_NOT_COMPLETE"; + case SBC_ERR_NO_MORE_ITEMS: + return "SBC_ERR_NO_MORE_ITEMS"; + case SBC_ERR_FILE_EXISTS: + return "SBC_ERR_FILE_EXISTS"; + case SBC_ERR_ACCESS_DENIED: + return "SBC_ERR_ACCESS_DENIED"; + } + + return "unknown sbcErr value"; +} + + +/** + * Tell whether the backend requires messaging to be set up + * for the backend to work correctly. + */ +bool smbconf_backend_requires_messaging(struct smbconf_ctx *ctx) +{ + return ctx->ops->requires_messaging(ctx); +} + +/** + * Tell whether the source is writeable. + */ +bool smbconf_is_writeable(struct smbconf_ctx *ctx) +{ + return ctx->ops->is_writeable(ctx); +} + +/** + * Close the configuration. + */ +void smbconf_shutdown(struct smbconf_ctx *ctx) +{ + talloc_free(ctx); +} + +/** + * Detect changes in the configuration. + * The given csn struct is filled with the current csn. + * smbconf_changed() can also be used for initial retrieval + * of the csn. + */ +bool smbconf_changed(struct smbconf_ctx *ctx, struct smbconf_csn *csn, + const char *service, const char *param) +{ + struct smbconf_csn old_csn; + + if (csn == NULL) { + return false; + } + + old_csn = *csn; + + ctx->ops->get_csn(ctx, csn, service, param); + return (csn->csn != old_csn.csn); +} + +/** + * Drop the whole configuration (restarting empty). + */ +sbcErr smbconf_drop(struct smbconf_ctx *ctx) +{ + return ctx->ops->drop(ctx); +} + +/** + * Get the whole configuration as lists of strings with counts: + * + * num_shares : number of shares + * share_names : list of length num_shares of share names + * num_params : list of length num_shares of parameter counts for each share + * param_names : list of lists of parameter names for each share + * param_values : list of lists of parameter values for each share + */ +sbcErr smbconf_get_config(struct smbconf_ctx *ctx, + TALLOC_CTX *mem_ctx, + uint32_t *num_shares, + struct smbconf_service ***services) +{ + sbcErr err; + TALLOC_CTX *tmp_ctx = NULL; + uint32_t tmp_num_shares; + char **tmp_share_names; + struct smbconf_service **tmp_services; + uint32_t count; + + if ((num_shares == NULL) || (services == NULL)) { + err = SBC_ERR_INVALID_PARAM; + goto done; + } + + tmp_ctx = talloc_stackframe(); + + err = smbconf_get_share_names(ctx, tmp_ctx, &tmp_num_shares, + &tmp_share_names); + if (!SBC_ERROR_IS_OK(err)) { + goto done; + } + + tmp_services = talloc_array(tmp_ctx, struct smbconf_service *, + tmp_num_shares); + if (tmp_services == NULL) { + err = SBC_ERR_NOMEM; + goto done; + } + + for (count = 0; count < tmp_num_shares; count++) { + err = smbconf_get_share(ctx, tmp_services, + tmp_share_names[count], + &tmp_services[count]); + if (!SBC_ERROR_IS_OK(err)) { + goto done; + } + } + + err = SBC_ERR_OK; + + *num_shares = tmp_num_shares; + if (tmp_num_shares > 0) { + *services = talloc_move(mem_ctx, &tmp_services); + } else { + *services = NULL; + } + +done: + talloc_free(tmp_ctx); + return err; +} + +/** + * get the list of share names defined in the configuration. + */ +sbcErr smbconf_get_share_names(struct smbconf_ctx *ctx, + TALLOC_CTX *mem_ctx, + uint32_t *num_shares, + char ***share_names) +{ + return ctx->ops->get_share_names(ctx, mem_ctx, num_shares, + share_names); +} + +/** + * check if a share/service of a given name exists + */ +bool smbconf_share_exists(struct smbconf_ctx *ctx, + const char *servicename) +{ + return ctx->ops->share_exists(ctx, servicename); +} + +/** + * Add a service if it does not already exist. + */ +sbcErr smbconf_create_share(struct smbconf_ctx *ctx, + const char *servicename) +{ + if ((servicename != NULL) && smbconf_share_exists(ctx, servicename)) { + return SBC_ERR_FILE_EXISTS; + } + + return ctx->ops->create_share(ctx, servicename); +} + +/** + * create and set the definition for a new share (service). + */ +sbcErr smbconf_create_set_share(struct smbconf_ctx *ctx, + struct smbconf_service *service) +{ + sbcErr err, err2; + int i; + uint32_t num_includes = 0; + char **includes = NULL; + TALLOC_CTX *tmp_ctx = NULL; + + if ((service->name != NULL) && smbconf_share_exists(ctx, service->name)) + { + return SBC_ERR_FILE_EXISTS; + } + + err = smbconf_transaction_start(ctx); + if (!SBC_ERROR_IS_OK(err)) { + return err; + } + + tmp_ctx = talloc_stackframe(); + + err = smbconf_create_share(ctx, service->name); + if (!SBC_ERROR_IS_OK(err)) { + goto cancel; + } + + for (i = 0; i < service->num_params; i++) { + if (strequal(service->param_names[i], "include")) { + includes = talloc_realloc(tmp_ctx, includes, char *, + num_includes+1); + if (includes == NULL) { + err = SBC_ERR_NOMEM; + goto cancel; + } + includes[num_includes] = talloc_strdup(includes, + service->param_values[i]); + if (includes[num_includes] == NULL) { + err = SBC_ERR_NOMEM; + goto cancel; + } + num_includes++; + } else { + err = smbconf_set_parameter(ctx, + service->name, + service->param_names[i], + service->param_values[i]); + if (!SBC_ERROR_IS_OK(err)) { + goto cancel; + } + } + } + + err = smbconf_set_includes(ctx, service->name, num_includes, + discard_const_p(const char *, includes)); + if (!SBC_ERROR_IS_OK(err)) { + goto cancel; + } + + err = smbconf_transaction_commit(ctx); + + goto done; + +cancel: + err2 = smbconf_transaction_cancel(ctx); + if (!SBC_ERROR_IS_OK(err2)) { + DEBUG(5, (__location__ ": Error cancelling transaction: %s\n", + sbcErrorString(err2))); + } + +done: + talloc_free(tmp_ctx); + return err; +} + +/** + * get a definition of a share (service) from configuration. + */ +sbcErr smbconf_get_share(struct smbconf_ctx *ctx, + TALLOC_CTX *mem_ctx, + const char *servicename, + struct smbconf_service **service) +{ + return ctx->ops->get_share(ctx, mem_ctx, servicename, service); +} + +/** + * delete a service from configuration + */ +sbcErr smbconf_delete_share(struct smbconf_ctx *ctx, const char *servicename) +{ + if (!smbconf_share_exists(ctx, servicename)) { + return SBC_ERR_NO_SUCH_SERVICE; + } + + return ctx->ops->delete_share(ctx, servicename); +} + +/** + * set a configuration parameter to the value provided. + */ +sbcErr smbconf_set_parameter(struct smbconf_ctx *ctx, + const char *service, + const char *param, + const char *valstr) +{ + return ctx->ops->set_parameter(ctx, service, param, valstr); +} + +/** + * Set a global parameter + * (i.e. a parameter in the [global] service). + * + * This also creates [global] when it does not exist. + */ +sbcErr smbconf_set_global_parameter(struct smbconf_ctx *ctx, + const char *param, const char *val) +{ + sbcErr err; + + err = smbconf_global_check(ctx); + if (!SBC_ERROR_IS_OK(err)) { + return err; + } + err = smbconf_set_parameter(ctx, GLOBAL_NAME, param, val); + + return err; +} + +/** + * get the value of a configuration parameter as a string + */ +sbcErr smbconf_get_parameter(struct smbconf_ctx *ctx, + TALLOC_CTX *mem_ctx, + const char *service, + const char *param, + char **valstr) +{ + if (valstr == NULL) { + return SBC_ERR_INVALID_PARAM; + } + + return ctx->ops->get_parameter(ctx, mem_ctx, service, param, valstr); +} + +/** + * Get the value of a global parameter. + * + * Create [global] if it does not exist. + */ +sbcErr smbconf_get_global_parameter(struct smbconf_ctx *ctx, + TALLOC_CTX *mem_ctx, + const char *param, + char **valstr) +{ + sbcErr err; + + err = smbconf_global_check(ctx); + if (!SBC_ERROR_IS_OK(err)) { + return err; + } + + err = smbconf_get_parameter(ctx, mem_ctx, GLOBAL_NAME, param, + valstr); + + return err; +} + +/** + * delete a parameter from configuration + */ +sbcErr smbconf_delete_parameter(struct smbconf_ctx *ctx, + const char *service, const char *param) +{ + return ctx->ops->delete_parameter(ctx, service, param); +} + +/** + * Delete a global parameter. + * + * Create [global] if it does not exist. + */ +sbcErr smbconf_delete_global_parameter(struct smbconf_ctx *ctx, + const char *param) +{ + sbcErr err; + + err = smbconf_global_check(ctx); + if (!SBC_ERROR_IS_OK(err)) { + return err; + } + err = smbconf_delete_parameter(ctx, GLOBAL_NAME, param); + + return err; +} + +sbcErr smbconf_get_includes(struct smbconf_ctx *ctx, + TALLOC_CTX *mem_ctx, + const char *service, + uint32_t *num_includes, char ***includes) +{ + return ctx->ops->get_includes(ctx, mem_ctx, service, num_includes, + includes); +} + +sbcErr smbconf_get_global_includes(struct smbconf_ctx *ctx, + TALLOC_CTX *mem_ctx, + uint32_t *num_includes, char ***includes) +{ + sbcErr err; + + err = smbconf_global_check(ctx); + if (!SBC_ERROR_IS_OK(err)) { + return err; + } + err = smbconf_get_includes(ctx, mem_ctx, GLOBAL_NAME, + num_includes, includes); + + return err; +} + +sbcErr smbconf_set_includes(struct smbconf_ctx *ctx, + const char *service, + uint32_t num_includes, const char **includes) +{ + return ctx->ops->set_includes(ctx, service, num_includes, includes); +} + +sbcErr smbconf_set_global_includes(struct smbconf_ctx *ctx, + uint32_t num_includes, + const char **includes) +{ + sbcErr err; + + err = smbconf_global_check(ctx); + if (!SBC_ERROR_IS_OK(err)) { + return err; + } + err = smbconf_set_includes(ctx, GLOBAL_NAME, + num_includes, includes); + + return err; +} + + +sbcErr smbconf_delete_includes(struct smbconf_ctx *ctx, const char *service) +{ + return ctx->ops->delete_includes(ctx, service); +} + +sbcErr smbconf_delete_global_includes(struct smbconf_ctx *ctx) +{ + sbcErr err; + + err = smbconf_global_check(ctx); + if (!SBC_ERROR_IS_OK(err)) { + return err; + } + err = smbconf_delete_includes(ctx, GLOBAL_NAME); + + return err; +} + +sbcErr smbconf_transaction_start(struct smbconf_ctx *ctx) +{ + return ctx->ops->transaction_start(ctx); +} + +sbcErr smbconf_transaction_commit(struct smbconf_ctx *ctx) +{ + return ctx->ops->transaction_commit(ctx); +} + +sbcErr smbconf_transaction_cancel(struct smbconf_ctx *ctx) +{ + return ctx->ops->transaction_cancel(ctx); +} diff --git a/lib/smbconf/smbconf.h b/lib/smbconf/smbconf.h new file mode 100644 index 0000000..e5f138f --- /dev/null +++ b/lib/smbconf/smbconf.h @@ -0,0 +1,505 @@ +/* + * Unix SMB/CIFS implementation. + * libsmbconf - Samba configuration library + * Copyright (C) Michael Adam 2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __LIBSMBCONF_H__ +#define __LIBSMBCONF_H__ + +/** + * @defgroup libsmbconf The smbconf API + * + * libsmbconf is a library to read or, based on the backend, modify the Samba + * configuration. + * + * @{ + */ + +/** + * @brief Status codes returned from smbconf functions + */ +enum _sbcErrType { + SBC_ERR_OK = 0, /**< Successful completion **/ + SBC_ERR_NOT_IMPLEMENTED, /**< Function not implemented **/ + SBC_ERR_NOT_SUPPORTED, /**< Function not supported **/ + SBC_ERR_UNKNOWN_FAILURE, /**< General failure **/ + SBC_ERR_NOMEM, /**< Memory allocation error **/ + SBC_ERR_INVALID_PARAM, /**< An Invalid parameter was supplied **/ + SBC_ERR_BADFILE, /**< A bad file was supplied **/ + SBC_ERR_NO_SUCH_SERVICE, /**< There is no such service provided **/ + SBC_ERR_IO_FAILURE, /**< There was an IO error **/ + SBC_ERR_CAN_NOT_COMPLETE,/**< Can not complete action **/ + SBC_ERR_NO_MORE_ITEMS, /**< No more items left **/ + SBC_ERR_FILE_EXISTS, /**< File already exists **/ + SBC_ERR_ACCESS_DENIED, /**< Access has been denied **/ +}; + +typedef enum _sbcErrType sbcErr; + +#define SBC_ERROR_IS_OK(x) ((x) == SBC_ERR_OK) +#define SBC_ERROR_EQUAL(x,y) ((x) == (y)) + +struct smbconf_ctx; + +/* the change sequence number */ +struct smbconf_csn { + uint64_t csn; +}; + +/** Information about a service */ +struct smbconf_service { + char *name; /**< The name of the share */ + uint32_t num_params; /**< List of length num_shares of parameter counts for each share */ + char **param_names; /**< List of lists of parameter names for each share */ + char **param_values; /**< List of lists of parameter values for each share */ +}; + +/* + * The smbconf API functions + */ + +/** + * @brief Translate an error value into a string + * + * @param error + * + * @return a pointer to a static string + **/ +const char *sbcErrorString(sbcErr error); + +/** + * @brief Check if the backend requires messaging to be set up. + * + * Tell whether the backend requires messaging to be set up + * for the backend to work correctly. + * + * @param[in] ctx The smbconf context to check. + * + * @return True if needed, false if not. + */ +bool smbconf_backend_requires_messaging(struct smbconf_ctx *ctx); + +/** + * @brief Tell whether the source is writeable. + * + * @param[in] ctx The smbconf context to check. + * + * @return True if it is writeable, false if not. + */ +bool smbconf_is_writeable(struct smbconf_ctx *ctx); + +/** + * @brief Close the configuration. + * + * @param[in] ctx The smbconf context to close. + */ +void smbconf_shutdown(struct smbconf_ctx *ctx); + +/** + * @brief Detect changes in the configuration. + * + * Get the change sequence number of the given service/parameter. Service and + * parameter strings may be NULL. + * + * The given change sequence number (csn) struct is filled with the current + * csn. smbconf_changed() can also be used for initial retrieval of the csn. + * + * @param[in] ctx The smbconf context to check for changes. + * + * @param[inout] csn The smbconf csn to be filled. + * + * @param[in] service The service name to check or NULL. + * + * @param[in] param The param to check or NULL. + * + * @return True if it has been changed, false if not. + */ +bool smbconf_changed(struct smbconf_ctx *ctx, struct smbconf_csn *csn, + const char *service, const char *param); + +/** + * @brief Drop the whole configuration (restarting empty). + * + * @param[in] ctx The smbconf context to drop the config. + * + * @return SBC_ERR_OK on success, a corresponding sbcErr if an + * error occurred. + */ +sbcErr smbconf_drop(struct smbconf_ctx *ctx); + +/** + * @brief Get the whole configuration as lists of strings with counts. + * + * @param[in] ctx The smbconf context to get the lists from. + * + * @param[in] mem_ctx The memory context to use. + * + * @param[in] num_shares A pointer to store the number of shares. + * + * @param[out] services A pointer to store the services. + * + * @return SBC_ERR_OK on success, a corresponding sbcErr if an + * error occurred. + * + * @see smbconf_service + */ +sbcErr smbconf_get_config(struct smbconf_ctx *ctx, + TALLOC_CTX *mem_ctx, + uint32_t *num_shares, + struct smbconf_service ***services); + +/** + * @brief Get the list of share names defined in the configuration. + * + * @param[in] ctx The smbconf context to use. + * + * @param[in] mem_ctx The memory context to use. + * + * @param[in] num_shares A pointer to store the number of shares. + * + * @param[in] share_names A pointer to store the share names. + * + * @return SBC_ERR_OK on success, a corresponding sbcErr if an + * error occurred. + */ +sbcErr smbconf_get_share_names(struct smbconf_ctx *ctx, + TALLOC_CTX *mem_ctx, + uint32_t *num_shares, + char ***share_names); + +/** + * @brief Check if a share/service of a given name exists. + * + * @param[in] ctx The smbconf context to use. + * + * @param[in] servicename The service name to check if it exists. + * + * @return True if it exists, false if not. + */ +bool smbconf_share_exists(struct smbconf_ctx *ctx, const char *servicename); + +/** + * @brief Add a service if it does not already exist. + * + * @param[in] ctx The smbconf context to use. + * + * @param[in] servicename The name of the service to add. + * + * @return SBC_ERR_OK on success, a corresponding sbcErr if an + * error occurred. + */ +sbcErr smbconf_create_share(struct smbconf_ctx *ctx, const char *servicename); + +/** + * @brief create and set the definition for a new service. + * + * @param[in] ctx The smbconf context to use. + * + * @param[in] service The definition for the added service. + * + * @return SBC_ERR_OK on success, a corresponding sbcErr if an + * error occurred. + */ +sbcErr smbconf_create_set_share(struct smbconf_ctx *ctx, + struct smbconf_service *service); + +/** + * @brief Get a definition of a share (service) from configuration. + * + * @param[in] ctx The smbconf context to use. + * + * @param[in] mem_ctx A memory context to allocate the result. + * + * @param[in] servicename The service name to get the information from. + * + * @param[out] service A pointer to store the service information about the + * share. + * + * @return SBC_ERR_OK on success, a corresponding sbcErr if an + * error occurred. + * + * @see smbconf_service + */ +sbcErr smbconf_get_share(struct smbconf_ctx *ctx, + TALLOC_CTX *mem_ctx, + const char *servicename, + struct smbconf_service **service); + +/** + * @brief Delete a service from configuration. + * + * @param[in] ctx The smbconf context to use. + * + * @param[in] servicename The service name to delete. + * + * @return SBC_ERR_OK on success, a corresponding sbcErr if an + * error occurred. + */ +sbcErr smbconf_delete_share(struct smbconf_ctx *ctx, + const char *servicename); + +/** + * @brief Set a configuration parameter to the value provided. + * + * @param[in] ctx The smbconf context to use. + * + * @param[in] service The service name to set the parameter. + * + * @param[in] param The name of the parameter to set. + * + * @param[in] valstr The value to set. + * + * @return SBC_ERR_OK on success, a corresponding sbcErr if an + * error occurred. + */ +sbcErr smbconf_set_parameter(struct smbconf_ctx *ctx, + const char *service, + const char *param, + const char *valstr); + +/** + * @brief Set a global configuration parameter to the value provided. + * + * This adds a paramet in the [global] service. It also creates [global] if it + * doesn't exist. + * + * @param[in] ctx The smbconf context to use. + * + * @param[in] param The name of the parameter to set. + * + * @param[in] val The value to set. + * + * @return SBC_ERR_OK on success, a corresponding sbcErr if an + * error occurred. + */ +sbcErr smbconf_set_global_parameter(struct smbconf_ctx *ctx, + const char *param, const char *val); + +/** + * @brief Get the value of a configuration parameter as a string. + * + * @param[in] ctx The smbconf context to use. + * + * @param[in] mem_ctx The memory context to allocate the string on. + * + * @param[in] service The name of the service where to find the parameter. + * + * @param[in] param The parameter to get. + * + * @param[out] valstr A pointer to store the value as a string. + * + * @return SBC_ERR_OK on success, a corresponding sbcErr if an + * error occurred. + */ +sbcErr smbconf_get_parameter(struct smbconf_ctx *ctx, + TALLOC_CTX *mem_ctx, + const char *service, + const char *param, + char **valstr); + +/** + * @brief Get the value of a global configuration parameter as a string. + * + * It also creates [global] if it doesn't exist. + * + * @param[in] ctx The smbconf context to use. + * + * @param[in] mem_ctx The memory context to allocate the string on. + * + * @param[in] param The parameter to get. + * + * @param[out] valstr A pointer to store the value as a string. + * + * @return SBC_ERR_OK on success, a corresponding sbcErr if an + * error occurred. + */ +sbcErr smbconf_get_global_parameter(struct smbconf_ctx *ctx, + TALLOC_CTX *mem_ctx, + const char *param, + char **valstr); + +/** + * @brief Delete a parameter from the configuration. + * + * @param[in] ctx The smbconf context to use. + * + * @param[in] service The service where the parameter can be found. + * + * @param[in] param The name of the parameter to delete. + * + * @return SBC_ERR_OK on success, a corresponding sbcErr if an + * error occurred. + */ +sbcErr smbconf_delete_parameter(struct smbconf_ctx *ctx, + const char *service, const char *param); + +/** + * @brief Delete a global parameter from the configuration. + * + * It also creates [global] if it doesn't exist. + * + * @param[in] ctx The smbconf context to use. + * + * @param[in] param The name of the parameter to delete. + * + * @return SBC_ERR_OK on success, a corresponding sbcErr if an + * error occurred. + */ +sbcErr smbconf_delete_global_parameter(struct smbconf_ctx *ctx, + const char *param); + +/** + * @brief Get the list of names of included files. + * + * @param[in] ctx The smbconf context to use. + * + * @param[in] mem_ctx The memory context to allocate the names. + * + * @param[in] service The service name to get the include files. + * + * @param[out] num_includes A pointer to store the number of included files. + * + * @param[out] includes A pointer to store the paths of the included files. + * + * @return SBC_ERR_OK on success, a corresponding sbcErr if an + * error occurred. + */ +sbcErr smbconf_get_includes(struct smbconf_ctx *ctx, + TALLOC_CTX *mem_ctx, + const char *service, + uint32_t *num_includes, char ***includes); + +/** + * @brief Get the list of globally included files. + * + * @param[in] ctx The smbconf context to use. + * + * @param[in] mem_ctx The memory context to allocate the names. + * + * @param[out] num_includes A pointer to store the number of included files. + * + * @param[out] includes A pointer to store the paths of the included files. + * + * @return SBC_ERR_OK on success, a corresponding sbcErr if an + * error occurred. + */ +sbcErr smbconf_get_global_includes(struct smbconf_ctx *ctx, + TALLOC_CTX *mem_ctx, + uint32_t *num_includes, char ***includes); + +/** + * @brief Set a list of config files to include on the given service. + * + * @param[in] ctx The smbconf context to use. + * + * @param[in] service The service to add includes. + * + * @param[in] num_includes The number of includes to set. + * + * @param[in] includes A list of paths to include. + * + * @return SBC_ERR_OK on success, a corresponding sbcErr if an + * error occurred. + */ +sbcErr smbconf_set_includes(struct smbconf_ctx *ctx, + const char *service, + uint32_t num_includes, const char **includes); + +/** + * @brief Set a list of config files to include globally. + * + * @param[in] ctx The smbconf context to use. + * + * @param[in] num_includes The number of includes to set. + * + * @param[in] includes A list of paths to include. + * + * @return SBC_ERR_OK on success, a corresponding sbcErr if an + * error occurred. + */ +sbcErr smbconf_set_global_includes(struct smbconf_ctx *ctx, + uint32_t num_includes, + const char **includes); + +/** + * @brief Delete include parameter on the given service. + * + * @param[in] ctx The smbconf context to use. + * + * @param[in] service The name of the service to delete the includes from. + * + * @return SBC_ERR_OK on success, a corresponding sbcErr if an + * error occurred. + */ +sbcErr smbconf_delete_includes(struct smbconf_ctx *ctx, const char *service); + +/** + * @brief Delete include parameter from the global service. + * + * @param[in] ctx The smbconf context to use. + * + * @return SBC_ERR_OK on success, a corresponding sbcErr if an + * error occurred. + */ +sbcErr smbconf_delete_global_includes(struct smbconf_ctx *ctx); + +/** + * @brief Start a transaction on the configuration backend. + * + * Transactions are exposed in order to make it possible + * to create atomic compound writing commands. + * + * @param[in] ctx The smbconf context to start the transaction. + * + * @return SBC_ERR_OK on success, a corresponding sbcErr if an + * error occurred. + */ +sbcErr smbconf_transaction_start(struct smbconf_ctx *ctx); + +/** + * @brief Commit a transaction on the configuration backend. + * + * Transactions are exposed in order to make it possible + * to create atomic compound writing commands. + * + * @param[in] ctx The smbconf context to commit the transaction. + * + * @return SBC_ERR_OK on success, a corresponding sbcErr if an + * error occurred. + * + * @see smbconf_transaction_start() + */ +sbcErr smbconf_transaction_commit(struct smbconf_ctx *ctx); + +/** + * @brief Cancel a transaction on the configuration backend. + * + * Transactions are exposed in order to make it possible + * to create atomic compound writing commands. + * + * @param[in] ctx The smbconf context to cancel the transaction. + * + * @return SBC_ERR_OK on success, a corresponding sbcErr if an + * error occurred. + * + * @see smbconf_transaction_start() + */ +sbcErr smbconf_transaction_cancel(struct smbconf_ctx *ctx); + +/* @} ******************************************************************/ + +#endif /* _LIBSMBCONF_H_ */ diff --git a/lib/smbconf/smbconf_private.h b/lib/smbconf/smbconf_private.h new file mode 100644 index 0000000..e768c30 --- /dev/null +++ b/lib/smbconf/smbconf_private.h @@ -0,0 +1,96 @@ +/* + * Unix SMB/CIFS implementation. + * libsmbconf - Samba configuration library + * Copyright (C) Michael Adam 2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __LIBSMBCONF_PRIVATE_H__ +#define __LIBSMBCONF_PRIVATE_H__ + +#ifndef GLOBAL_NAME +#define GLOBAL_NAME "global" +#endif + +#include "lib/smbconf/smbconf.h" + +struct smbconf_ops { + sbcErr (*init)(struct smbconf_ctx *ctx, const char *path); + int (*shutdown)(struct smbconf_ctx *ctx); + bool (*requires_messaging)(struct smbconf_ctx *ctx); + bool (*is_writeable)(struct smbconf_ctx *ctx); + sbcErr (*open_conf)(struct smbconf_ctx *ctx); + int (*close_conf)(struct smbconf_ctx *ctx); + void (*get_csn)(struct smbconf_ctx *ctx, struct smbconf_csn *csn, + const char *service, const char *param); + sbcErr (*drop)(struct smbconf_ctx *ctx); + sbcErr (*get_share_names)(struct smbconf_ctx *ctx, + TALLOC_CTX *mem_ctx, + uint32_t *num_shares, + char ***share_names); + bool (*share_exists)(struct smbconf_ctx *ctx, const char *service); + sbcErr (*create_share)(struct smbconf_ctx *ctx, const char *service); + sbcErr (*get_share)(struct smbconf_ctx *ctx, + TALLOC_CTX *mem_ctx, + const char *servicename, + struct smbconf_service **service); + sbcErr (*delete_share)(struct smbconf_ctx *ctx, + const char *servicename); + sbcErr (*set_parameter)(struct smbconf_ctx *ctx, + const char *service, + const char *param, + const char *valstr); + sbcErr (*get_parameter)(struct smbconf_ctx *ctx, + TALLOC_CTX *mem_ctx, + const char *service, + const char *param, + char **valstr); + sbcErr (*delete_parameter)(struct smbconf_ctx *ctx, + const char *service, const char *param); + sbcErr (*get_includes)(struct smbconf_ctx *ctx, + TALLOC_CTX *mem_ctx, + const char *service, + uint32_t *num_includes, char ***includes); + sbcErr (*set_includes)(struct smbconf_ctx *ctx, + const char *service, + uint32_t num_includes, const char **includes); + sbcErr (*delete_includes)(struct smbconf_ctx *ctx, + const char *service); + sbcErr (*transaction_start)(struct smbconf_ctx *ctx); + sbcErr (*transaction_commit)(struct smbconf_ctx *ctx); + sbcErr (*transaction_cancel)(struct smbconf_ctx *ctx); +}; + +struct smbconf_ctx { + const char *path; + struct smbconf_ops *ops; + void *data; /* private data for use in backends */ +}; + +sbcErr smbconf_init_internal(TALLOC_CTX *mem_ctx, struct smbconf_ctx **conf_ctx, + const char *path, struct smbconf_ops *ops); + +sbcErr smbconf_add_string_to_array(TALLOC_CTX *mem_ctx, + char ***array, + uint32_t count, + const char *string); + +bool smbconf_find_in_array(const char *string, char **list, + uint32_t num_entries, uint32_t *entry); + +bool smbconf_reverse_find_in_array(const char *string, char **list, + uint32_t num_entries, uint32_t *entry); + +#endif diff --git a/lib/smbconf/smbconf_txt.c b/lib/smbconf/smbconf_txt.c new file mode 100644 index 0000000..70a35ec --- /dev/null +++ b/lib/smbconf/smbconf_txt.c @@ -0,0 +1,682 @@ +/* + * Unix SMB/CIFS implementation. + * libsmbconf - Samba configuration library, text backend + * Copyright (C) Michael Adam 2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + * This is a sample implementation of a libsmbconf text backend + * using the params.c parser. + * + * It is read only. + * Don't expect brilliant performance, since it is not hashing the lists. + */ + +#include "includes.h" +#include "smbconf_private.h" +#include "lib/smbconf/smbconf_txt.h" + +struct txt_cache { + uint32_t current_share; + uint32_t num_shares; + char **share_names; + uint32_t *num_params; + char ***param_names; + char ***param_values; +}; + +struct txt_private_data { + struct txt_cache *cache; + uint64_t csn; + bool verbatim; +}; + +/********************************************************************** + * + * helper functions + * + **********************************************************************/ + +/** + * a convenience helper to cast the private data structure + */ +static struct txt_private_data *pd(struct smbconf_ctx *ctx) +{ + return (struct txt_private_data *)(ctx->data); +} + +static bool smbconf_txt_do_section(const char *section, void *private_data) +{ + sbcErr err; + uint32_t idx; + struct txt_private_data *tpd = (struct txt_private_data *)private_data; + struct txt_cache *cache = tpd->cache; + + if (smbconf_find_in_array(section, cache->share_names, + cache->num_shares, &idx)) + { + cache->current_share = idx; + return true; + } + + err = smbconf_add_string_to_array(cache, &(cache->share_names), + cache->num_shares, section); + if (!SBC_ERROR_IS_OK(err)) { + return false; + } + cache->current_share = cache->num_shares; + cache->num_shares++; + + cache->param_names = talloc_realloc(cache, + cache->param_names, + char **, + cache->num_shares); + if (cache->param_names == NULL) { + return false; + } + cache->param_names[cache->current_share] = NULL; + + cache->param_values = talloc_realloc(cache, + cache->param_values, + char **, + cache->num_shares); + if (cache->param_values == NULL) { + return false; + } + cache->param_values[cache->current_share] = NULL; + + cache->num_params = talloc_realloc(cache, + cache->num_params, + uint32_t, + cache->num_shares); + if (cache->num_params == NULL) { + return false; + } + cache->num_params[cache->current_share] = 0; + + return true; +} + +static bool smbconf_txt_do_parameter(const char *param_name, + const char *param_value, + void *private_data) +{ + sbcErr err; + char **param_names, **param_values; + uint32_t num_params; + uint32_t idx; + struct txt_private_data *tpd = (struct txt_private_data *)private_data; + struct txt_cache *cache = tpd->cache; + + if (cache->num_shares == 0) { + /* + * not in any share yet, + * initialize the "empty" section (NULL): + * parameters without a previous [section] are stored here. + */ + if (!smbconf_txt_do_section(NULL, private_data)) { + return false; + } + } + + param_names = cache->param_names[cache->current_share]; + param_values = cache->param_values[cache->current_share]; + num_params = cache->num_params[cache->current_share]; + + if (!(tpd->verbatim) && + smbconf_find_in_array(param_name, param_names, num_params, &idx)) + { + talloc_free(param_values[idx]); + param_values[idx] = talloc_strdup(cache, param_value); + if (param_values[idx] == NULL) { + return false; + } + return true; + } + err = smbconf_add_string_to_array(cache, + &(cache->param_names[cache->current_share]), + num_params, param_name); + if (!SBC_ERROR_IS_OK(err)) { + return false; + } + err = smbconf_add_string_to_array(cache, + &(cache->param_values[cache->current_share]), + num_params, param_value); + cache->num_params[cache->current_share]++; + return SBC_ERROR_IS_OK(err); +} + +static void smbconf_txt_flush_cache(struct smbconf_ctx *ctx) +{ + talloc_free(pd(ctx)->cache); + pd(ctx)->cache = NULL; +} + +static sbcErr smbconf_txt_init_cache(struct smbconf_ctx *ctx) +{ + if (pd(ctx)->cache != NULL) { + smbconf_txt_flush_cache(ctx); + } + + pd(ctx)->cache = talloc_zero(pd(ctx), struct txt_cache); + + if (pd(ctx)->cache == NULL) { + return SBC_ERR_NOMEM; + } + + return SBC_ERR_OK; +} + +static sbcErr smbconf_txt_load_file(struct smbconf_ctx *ctx) +{ + sbcErr err; + uint64_t new_csn; + int rc; + struct timespec mt = {0}; + + if (!file_exist(ctx->path)) { + return SBC_ERR_BADFILE; + } + + rc = file_modtime(ctx->path, &mt); + if (rc != 0) { + /* + * Not worth mapping errno returned + * in rc to SBC_ERR_XXX. Just assume + * access denied. + */ + return SBC_ERR_ACCESS_DENIED; + } + new_csn = (uint64_t)mt.tv_sec; + if (new_csn == pd(ctx)->csn) { + return SBC_ERR_OK; + } + + err = smbconf_txt_init_cache(ctx); + if (!SBC_ERROR_IS_OK(err)) { + return err; + } + + if (!pm_process(ctx->path, smbconf_txt_do_section, + smbconf_txt_do_parameter, pd(ctx))) + { + return SBC_ERR_CAN_NOT_COMPLETE; + } + + pd(ctx)->csn = new_csn; + + return SBC_ERR_OK; +} + + +/********************************************************************** + * + * smbconf operations: text backend implementations + * + **********************************************************************/ + +/** + * initialize the text based smbconf backend + */ +static sbcErr smbconf_txt_init(struct smbconf_ctx *ctx, const char *path) +{ + if (path == NULL) { + return SBC_ERR_BADFILE; + } + ctx->path = talloc_strdup(ctx, path); + if (ctx->path == NULL) { + return SBC_ERR_NOMEM; + } + + ctx->data = talloc_zero(ctx, struct txt_private_data); + if (ctx->data == NULL) { + return SBC_ERR_NOMEM; + } + + pd(ctx)->verbatim = true; + + return SBC_ERR_OK; +} + +static int smbconf_txt_shutdown(struct smbconf_ctx *ctx) +{ + return ctx->ops->close_conf(ctx); +} + +static bool smbconf_txt_requires_messaging(struct smbconf_ctx *ctx) +{ + return false; +} + +static bool smbconf_txt_is_writeable(struct smbconf_ctx *ctx) +{ + /* no write support in this backend yet... */ + return false; +} + +static sbcErr smbconf_txt_open(struct smbconf_ctx *ctx) +{ + return smbconf_txt_load_file(ctx); +} + +static int smbconf_txt_close(struct smbconf_ctx *ctx) +{ + smbconf_txt_flush_cache(ctx); + return 0; +} + +/** + * Get the change sequence number of the given service/parameter. + * service and parameter strings may be NULL. + */ +static void smbconf_txt_get_csn(struct smbconf_ctx *ctx, + struct smbconf_csn *csn, + const char *service, const char *param) +{ + struct timespec mt = {0}; + + if (csn == NULL) { + return; + } + + (void)file_modtime(ctx->path, &mt); + csn->csn = (uint64_t)mt.tv_sec; +} + +/** + * Drop the whole configuration (restarting empty) + */ +static sbcErr smbconf_txt_drop(struct smbconf_ctx *ctx) +{ + return SBC_ERR_NOT_SUPPORTED; +} + +/** + * get the list of share names defined in the configuration. + */ +static sbcErr smbconf_txt_get_share_names(struct smbconf_ctx *ctx, + TALLOC_CTX *mem_ctx, + uint32_t *num_shares, + char ***share_names) +{ + uint32_t count; + uint32_t added_count = 0; + TALLOC_CTX *tmp_ctx = NULL; + sbcErr err = SBC_ERR_OK; + char **tmp_share_names = NULL; + + if ((num_shares == NULL) || (share_names == NULL)) { + return SBC_ERR_INVALID_PARAM; + } + + err = smbconf_txt_load_file(ctx); + if (!SBC_ERROR_IS_OK(err)) { + return err; + } + + tmp_ctx = talloc_stackframe(); + + /* make sure "global" is always listed first, + * possibly after NULL section */ + + if (smbconf_share_exists(ctx, NULL)) { + err = smbconf_add_string_to_array(tmp_ctx, &tmp_share_names, + 0, NULL); + if (!SBC_ERROR_IS_OK(err)) { + goto done; + } + added_count++; + } + + if (smbconf_share_exists(ctx, GLOBAL_NAME)) { + err = smbconf_add_string_to_array(tmp_ctx, &tmp_share_names, + added_count, GLOBAL_NAME); + if (!SBC_ERROR_IS_OK(err)) { + goto done; + } + added_count++; + } + + for (count = 0; count < pd(ctx)->cache->num_shares; count++) { + if (strequal(pd(ctx)->cache->share_names[count], GLOBAL_NAME) || + (pd(ctx)->cache->share_names[count] == NULL)) + { + continue; + } + + err = smbconf_add_string_to_array(tmp_ctx, &tmp_share_names, + added_count, + pd(ctx)->cache->share_names[count]); + if (!SBC_ERROR_IS_OK(err)) { + goto done; + } + added_count++; + } + + *num_shares = added_count; + if (added_count > 0) { + *share_names = talloc_move(mem_ctx, &tmp_share_names); + } else { + *share_names = NULL; + } + +done: + talloc_free(tmp_ctx); + return err; +} + +/** + * check if a share/service of a given name exists + */ +static bool smbconf_txt_share_exists(struct smbconf_ctx *ctx, + const char *servicename) +{ + sbcErr err; + + err = smbconf_txt_load_file(ctx); + if (!SBC_ERROR_IS_OK(err)) { + return false; + } + + return smbconf_find_in_array(servicename, + pd(ctx)->cache->share_names, + pd(ctx)->cache->num_shares, NULL); +} + +/** + * Add a service if it does not already exist + */ +static sbcErr smbconf_txt_create_share(struct smbconf_ctx *ctx, + const char *servicename) +{ + return SBC_ERR_NOT_SUPPORTED; +} + +/** + * get a definition of a share (service) from configuration. + */ +static sbcErr smbconf_txt_get_share(struct smbconf_ctx *ctx, + TALLOC_CTX *mem_ctx, + const char *servicename, + struct smbconf_service **service) +{ + sbcErr err; + uint32_t sidx, count; + bool found; + TALLOC_CTX *tmp_ctx = NULL; + struct smbconf_service *tmp_service = NULL; + + err = smbconf_txt_load_file(ctx); + if (!SBC_ERROR_IS_OK(err)) { + return err; + } + + found = smbconf_find_in_array(servicename, + pd(ctx)->cache->share_names, + pd(ctx)->cache->num_shares, + &sidx); + if (!found) { + return SBC_ERR_NO_SUCH_SERVICE; + } + + tmp_ctx = talloc_stackframe(); + + tmp_service = talloc_zero(tmp_ctx, struct smbconf_service); + if (tmp_service == NULL) { + err = SBC_ERR_NOMEM; + goto done; + } + + if (servicename != NULL) { + tmp_service->name = talloc_strdup(tmp_service, servicename); + if (tmp_service->name == NULL) { + err = SBC_ERR_NOMEM; + goto done; + } + } + + for (count = 0; count < pd(ctx)->cache->num_params[sidx]; count++) { + err = smbconf_add_string_to_array(tmp_service, + &(tmp_service->param_names), + count, + pd(ctx)->cache->param_names[sidx][count]); + if (!SBC_ERROR_IS_OK(err)) { + goto done; + } + err = smbconf_add_string_to_array(tmp_service, + &(tmp_service->param_values), + count, + pd(ctx)->cache->param_values[sidx][count]); + if (!SBC_ERROR_IS_OK(err)) { + goto done; + } + } + + tmp_service->num_params = count; + *service = talloc_move(mem_ctx, &tmp_service); + +done: + talloc_free(tmp_ctx); + return err; +} + +/** + * delete a service from configuration + */ +static sbcErr smbconf_txt_delete_share(struct smbconf_ctx *ctx, + const char *servicename) +{ + return SBC_ERR_NOT_SUPPORTED; +} + +/** + * set a configuration parameter to the value provided. + */ +static sbcErr smbconf_txt_set_parameter(struct smbconf_ctx *ctx, + const char *service, + const char *param, + const char *valstr) +{ + return SBC_ERR_NOT_SUPPORTED; +} + +/** + * get the value of a configuration parameter as a string + */ +static sbcErr smbconf_txt_get_parameter(struct smbconf_ctx *ctx, + TALLOC_CTX *mem_ctx, + const char *service, + const char *param, + char **valstr) +{ + sbcErr err; + bool found; + uint32_t share_index, param_index; + + err = smbconf_txt_load_file(ctx); + if (!SBC_ERROR_IS_OK(err)) { + return err; + } + + found = smbconf_find_in_array(service, + pd(ctx)->cache->share_names, + pd(ctx)->cache->num_shares, + &share_index); + if (!found) { + return SBC_ERR_NO_SUCH_SERVICE; + } + + found = smbconf_reverse_find_in_array(param, + pd(ctx)->cache->param_names[share_index], + pd(ctx)->cache->num_params[share_index], + ¶m_index); + if (!found) { + return SBC_ERR_INVALID_PARAM; + } + + *valstr = talloc_strdup(mem_ctx, + pd(ctx)->cache->param_values[share_index][param_index]); + + if (*valstr == NULL) { + return SBC_ERR_NOMEM; + } + + return SBC_ERR_OK; +} + +/** + * delete a parameter from configuration + */ +static sbcErr smbconf_txt_delete_parameter(struct smbconf_ctx *ctx, + const char *service, + const char *param) +{ + return SBC_ERR_NOT_SUPPORTED; +} + +static sbcErr smbconf_txt_get_includes(struct smbconf_ctx *ctx, + TALLOC_CTX *mem_ctx, + const char *service, + uint32_t *num_includes, + char ***includes) +{ + sbcErr err; + bool found; + uint32_t sidx, count; + TALLOC_CTX *tmp_ctx = NULL; + uint32_t tmp_num_includes = 0; + char **tmp_includes = NULL; + + err = smbconf_txt_load_file(ctx); + if (!SBC_ERROR_IS_OK(err)) { + return err; + } + + found = smbconf_find_in_array(service, + pd(ctx)->cache->share_names, + pd(ctx)->cache->num_shares, + &sidx); + if (!found) { + return SBC_ERR_NO_SUCH_SERVICE; + } + + tmp_ctx = talloc_stackframe(); + + for (count = 0; count < pd(ctx)->cache->num_params[sidx]; count++) { + if (strequal(pd(ctx)->cache->param_names[sidx][count], + "include")) + { + err = smbconf_add_string_to_array(tmp_ctx, + &tmp_includes, + tmp_num_includes, + pd(ctx)->cache->param_values[sidx][count]); + if (!SBC_ERROR_IS_OK(err)) { + goto done; + } + tmp_num_includes++; + } + } + + *num_includes = tmp_num_includes; + if (*num_includes > 0) { + *includes = talloc_move(mem_ctx, &tmp_includes); + if (*includes == NULL) { + err = SBC_ERR_NOMEM; + goto done; + } + } else { + *includes = NULL; + } + + err = SBC_ERR_OK; + +done: + talloc_free(tmp_ctx); + return err; +} + +static sbcErr smbconf_txt_set_includes(struct smbconf_ctx *ctx, + const char *service, + uint32_t num_includes, + const char **includes) +{ + return SBC_ERR_NOT_SUPPORTED; +} + +static sbcErr smbconf_txt_delete_includes(struct smbconf_ctx *ctx, + const char *service) +{ + return SBC_ERR_NOT_SUPPORTED; +} + +static sbcErr smbconf_txt_transaction_start(struct smbconf_ctx *ctx) +{ + return SBC_ERR_OK; +} + +static sbcErr smbconf_txt_transaction_commit(struct smbconf_ctx *ctx) +{ + return SBC_ERR_OK; +} + +static sbcErr smbconf_txt_transaction_cancel(struct smbconf_ctx *ctx) +{ + return SBC_ERR_OK; +} + +static struct smbconf_ops smbconf_ops_txt = { + .init = smbconf_txt_init, + .shutdown = smbconf_txt_shutdown, + .requires_messaging = smbconf_txt_requires_messaging, + .is_writeable = smbconf_txt_is_writeable, + .open_conf = smbconf_txt_open, + .close_conf = smbconf_txt_close, + .get_csn = smbconf_txt_get_csn, + .drop = smbconf_txt_drop, + .get_share_names = smbconf_txt_get_share_names, + .share_exists = smbconf_txt_share_exists, + .create_share = smbconf_txt_create_share, + .get_share = smbconf_txt_get_share, + .delete_share = smbconf_txt_delete_share, + .set_parameter = smbconf_txt_set_parameter, + .get_parameter = smbconf_txt_get_parameter, + .delete_parameter = smbconf_txt_delete_parameter, + .get_includes = smbconf_txt_get_includes, + .set_includes = smbconf_txt_set_includes, + .delete_includes = smbconf_txt_delete_includes, + .transaction_start = smbconf_txt_transaction_start, + .transaction_commit = smbconf_txt_transaction_commit, + .transaction_cancel = smbconf_txt_transaction_cancel, +}; + + +/** + * initialize the smbconf text backend + * the only function that is exported from this module + */ +sbcErr smbconf_init_txt(TALLOC_CTX *mem_ctx, + struct smbconf_ctx **conf_ctx, + const char *path) +{ + sbcErr err; + + err = smbconf_init_internal(mem_ctx, conf_ctx, path, &smbconf_ops_txt); + if (!SBC_ERROR_IS_OK(err)) { + return err; + } + + return smbconf_txt_load_file(*conf_ctx); +} diff --git a/lib/smbconf/smbconf_txt.h b/lib/smbconf/smbconf_txt.h new file mode 100644 index 0000000..72d6207 --- /dev/null +++ b/lib/smbconf/smbconf_txt.h @@ -0,0 +1,33 @@ +/* + * Unix SMB/CIFS implementation. + * libsmbconf - Samba configuration library + * Copyright (C) Michael Adam 2009 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __LIBSMBCONF_TXT_H__ +#define __LIBSMBCONF_TXT_H__ + +struct smbconf_ctx; + +/** + * initialization functions for the text/file backend modules + */ + +sbcErr smbconf_init_txt(TALLOC_CTX *mem_ctx, + struct smbconf_ctx **conf_ctx, + const char *path); + +#endif /* _LIBSMBCONF_TXT_H_ */ diff --git a/lib/smbconf/smbconf_util.c b/lib/smbconf/smbconf_util.c new file mode 100644 index 0000000..86a9598 --- /dev/null +++ b/lib/smbconf/smbconf_util.c @@ -0,0 +1,151 @@ +/* + * Unix SMB/CIFS implementation. + * libsmbconf - Samba configuration library, utility functions + * Copyright (C) Michael Adam 2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "smbconf_private.h" + + +static int smbconf_destroy_ctx(struct smbconf_ctx *ctx) +{ + return ctx->ops->shutdown(ctx); +} + +/** + * Initialize the configuration. + * + * This should be the first function in a sequence of calls to smbconf + * functions: + * + * Upon success, this creates and returns the conf context + * that should be passed around in subsequent calls to the other + * smbconf functions. + * + * After the work with the configuration is completed, smbconf_shutdown() + * should be called. + */ +sbcErr smbconf_init_internal(TALLOC_CTX *mem_ctx, struct smbconf_ctx **conf_ctx, + const char *path, struct smbconf_ops *ops) +{ + sbcErr err = SBC_ERR_OK; + struct smbconf_ctx *ctx; + + if (conf_ctx == NULL) { + return SBC_ERR_INVALID_PARAM; + } + + ctx = talloc_zero(mem_ctx, struct smbconf_ctx); + if (ctx == NULL) { + return SBC_ERR_NOMEM; + } + + ctx->ops = ops; + + err = ctx->ops->init(ctx, path); + if (!SBC_ERROR_IS_OK(err)) { + goto fail; + } + + talloc_set_destructor(ctx, smbconf_destroy_ctx); + + *conf_ctx = ctx; + return err; + +fail: + talloc_free(ctx); + return err; +} + + +/** + * add a string to a talloced array of strings. + */ +sbcErr smbconf_add_string_to_array(TALLOC_CTX *mem_ctx, + char ***array, + uint32_t count, + const char *string) +{ + char **new_array = NULL; + + if (array == NULL) { + return SBC_ERR_INVALID_PARAM; + } + + new_array = talloc_realloc(mem_ctx, *array, char *, count + 1); + if (new_array == NULL) { + return SBC_ERR_NOMEM; + } + + if (string == NULL) { + new_array[count] = NULL; + } else { + new_array[count] = talloc_strdup(new_array, string); + if (new_array[count] == NULL) { + talloc_free(new_array); + return SBC_ERR_NOMEM; + } + } + + *array = new_array; + + return SBC_ERR_OK; +} + +bool smbconf_find_in_array(const char *string, char **list, + uint32_t num_entries, uint32_t *entry) +{ + uint32_t i; + + if (list == NULL) { + return false; + } + + for (i = 0; i < num_entries; i++) { + if (((string == NULL) && (list[i] == NULL)) || + strequal(string, list[i])) + { + if (entry != NULL) { + *entry = i; + } + return true; + } + } + + return false; +} + +bool smbconf_reverse_find_in_array(const char *string, char **list, + uint32_t num_entries, uint32_t *entry) +{ + int32_t i; + + if ((string == NULL) || (list == NULL) || (num_entries == 0)) { + return false; + } + + for (i = num_entries - 1; i >= 0; i--) { + if (strequal(string, list[i])) { + if (entry != NULL) { + *entry = i; + } + return true; + } + } + + return false; +} diff --git a/lib/smbconf/wscript_build b/lib/smbconf/wscript_build new file mode 100644 index 0000000..97d6b18 --- /dev/null +++ b/lib/smbconf/wscript_build @@ -0,0 +1,10 @@ +bld.SAMBA_SUBSYSTEM('LIBSMBCONF', + source='smbconf.c smbconf_txt.c smbconf_util.c', + deps='talloc sendfile' + ) + +bld.SAMBA3_PYTHON('pysmbconf', + source='pysmbconf.c', + public_deps=' '.join(['samba-util', 'tdb', 'talloc', 'smbconf']), + realname='samba/smbconf.so' + ) |