src/python.c

Go to the documentation of this file.
00001 /*
00002  * Author: Thomas Wouters <thomas@xs4all.net>
00003  *
00004  * Copyright (c) 2001-2002 Thomas Wouters
00005  *   All rights reserved.
00006  *
00007  * Loosely based on Markus Stenberg's python-patch to TinyMUSH 3.0.
00008  */
00009 
00010 #include "config.h"
00011 #include "db.h"
00012 #include "externs.h"
00013 #include "mudconf.h"
00014 #include "config.h"
00015 
00016 #ifdef USE_PYTHON
00017 #include "Python.h"
00018 
00019 static dbref cause = -1;
00020 static dbref who = -1;
00021 
00022 static long last_sec;
00023 static long last_run;
00024 
00025 static PyObject *MakeFakeStdout(dbref i);
00026 
00027 static PyObject *sysmod = NULL;
00028 static PyObject *muxmod = NULL;
00029 static PyObject *maindict = NULL;
00030 
00031 /* 0 = not started
00032   -1 = not functional (something severe happened)
00033    1 = started and ready to serve
00034 */
00035 static int MUXPy_State = 0;
00036 
00037 extern void raw_notify_raw(dbref, char *, char *);
00038 static void init_muxmodule(void);
00039 
00040 void MUXPy_ReportError(dbref who, int clearerr)
00041 {
00042         /* XXX report the traceback, somehow */
00043         if(clearerr)
00044                 PyErr_Clear();
00045 }
00046 
00047 void MUXPy_Abort(char *message)
00048 {
00049         /* Something went massively wrong. Disable the python runtime
00050            and report the error. */
00051 
00052         MUXPy_State = -1;
00053         STARTLOG(LOG_PROBLEMS, "PYTHON", NULL) {
00054                 log_text("Python Runtime disabled: ");
00055                 log_text(message);
00056                 ENDLOG;
00057         }
00058         if(PyErr_Occurred()) {
00059                 MUXPy_ReportError(-1, 1);
00060         }
00061         Py_XDECREF(maindict);
00062         Py_XDECREF(sysmod);
00063         Py_XDECREF(muxmod);
00064 }
00065 
00066 void MUXPy_Init(void)
00067 {
00068 
00069         PyObject *sys_path, *muxpy_path, *mainmod;
00070 
00071         if(MUXPy_State != 0)
00072                 return;
00073 
00074         Py_SetProgramName("netmux");
00075         Py_Initialize();
00076 
00077         init_muxmodule();
00078 
00079         if((mainmod = PyImport_ImportModule("__main__")) == NULL) {
00080                 MUXPy_Abort("Can't import '__main__' module");
00081                 return;
00082         }
00083 
00084         if((maindict = PyModule_GetDict(mainmod)) == NULL) {
00085                 MUXPy_Abort("Can't get module dict for '__main__'");
00086                 return;
00087         }
00088         Py_INCREF(maindict);
00089 
00090         if((sysmod = PyImport_ImportModule("sys")) == NULL) {
00091                 MUXPy_Abort("Can't import 'sys' module");
00092                 return;
00093         }
00094 
00095         sys_path = PyObject_GetAttrString(sysmod, "path");
00096         if(sys_path == NULL) {
00097                 MUXPy_Abort("Can't find sys.path to update!");
00098                 return;
00099         }
00100         muxpy_path = PyString_FromString("muxpy-lib");
00101         if(muxpy_path == NULL) {
00102                 Py_DECREF(sys_path);
00103                 MUXPy_Abort("Can't create muxpy-lib path string");
00104                 return;
00105         }
00106         if(PyList_Append(sys_path, muxpy_path) == -1) {
00107                 Py_DECREF(sys_path);
00108                 Py_DECREF(muxpy_path);
00109                 MUXPy_Abort("Can't append muxpy-lib path string to sys.path");
00110                 return;
00111         }
00112 
00113         if((muxmod = PyImport_ImportModule("mux")) == NULL) {
00114                 MUXPy_Abort("Can't import 'mux' module");
00115                 return;
00116         }
00117 
00118         STARTLOG(LOG_PROBLEMS, "PYTHON", NULL) {
00119                 log_text("Python Runtime Started.");
00120                 ENDLOG;
00121         }
00122         MUXPy_State = 1;
00123 }
00124 
00125 #define MAX_UPDATE_PER_SEC 3
00126 #define TICK(x) ((x) / (1000000/MAX_UPDATE_PER_SEC))
00127 
00128 static int shouldupdate()
00129 {
00130         struct timeval tv;
00131         struct timezone tz;
00132         int tick;
00133 
00134         if(gettimeofday(&tv, &tz) < 0)
00135                 return 0;
00136         if(tv.tv_sec == last_sec) {
00137                 tick = TICK(tv.tv_usec);
00138                 if(tick == last_run)
00139                         return 0;
00140         } else
00141                 tick = TICK(tv.tv_usec);
00142         last_sec = tv.tv_sec;
00143         last_run = tick;
00144         return 1;
00145 }
00146 
00147 void runPythonHook(char *hook)
00148 {
00149         PyObject *hookobj, *arglist, *result;
00150 
00151         if(MUXPy_State != 1)
00152                 return;
00153         hookobj = PyObject_GetAttrString(muxmod, hook);
00154         if(hookobj) {
00155                 if(PyCallable_Check(hookobj)) {
00156                         if((arglist = PyTuple_New(0)) == NULL) {
00157                                 /* Can't create/reuse empty tuple -- serious error. */
00158                                 MUXPy_Abort("Can't create empty tuple");
00159                                 return;
00160                         }
00161                         result = PyEval_CallObject(hookobj, arglist);
00162                         /* XXX use result */
00163                 } else {
00164                         MUXPy_ReportError(-1, 1);
00165                 }
00166         } else {
00167                 /* attr not found, possibly log it ? */
00168                 MUXPy_ReportError(-1, 1);
00169         }
00170 }
00171 
00172 void updatePython(void)
00173 {
00174         if(!shouldupdate())
00175                 return;
00176         runPythonHook("update");
00177 }
00178 
00179 void endPython(int result)
00180 {
00181         MUXPy_State = 0;
00182         Py_Exit(result);
00183         /* XXX: Insert this, instead of regular exit, to all places (ugh) */
00184         /* NOTREACHED */
00185 }
00186 
00187 static PyObject *setStdout(dbref player)
00188 {
00189         PyObject *stdout = MakeFakeStdout(player);
00190 
00191         if(stdout == NULL)
00192                 return NULL;
00193         if(PySys_SetObject("stdout", stdout) == -1) {
00194                 MUXPy_ReportError(player, 1);
00195                 return NULL;
00196         }
00197         if(PySys_SetObject("stderr", stdout) == -1) {
00198                 MUXPy_ReportError(player, 1);
00199                 return NULL;
00200         }
00201         return stdout;
00202 }
00203 
00204 static PyObject *evalString(char *runstring)
00205 {
00206         /* Run eval() on the code, and print result */
00207         PyObject *result = PyRun_String(runstring, Py_single_input,
00208                                                                         maindict, maindict);
00209 
00210         return result;
00211 }
00212 
00213 int update_whocause(void)
00214 {
00215         PyObject *tmp;
00216 
00217         if((tmp = PyInt_FromLong((long) who)) == NULL)
00218                 return -1;
00219 
00220         if(PyDict_SetItemString(maindict, "who", tmp) == -1) {
00221                 Py_DECREF(tmp);
00222                 return -1;
00223         }
00224         Py_DECREF(tmp);
00225 
00226         if((tmp = PyInt_FromLong((long) cause)) == NULL)
00227                 return -1;
00228         if(PyDict_SetItemString(maindict, "cause", tmp) == -1) {
00229                 Py_DECREF(tmp);
00230                 return -1;
00231         }
00232         Py_DECREF(tmp);
00233         return 0;
00234 }
00235 
00236 void do_python(dbref cwho, dbref ccause, int key, char *target)
00237 {
00238         PyObject *stdout, *result;
00239         dbref prev_cause = cause;
00240         dbref prev_who = who;
00241 
00242         if(MUXPy_State != 1)
00243                 return;
00244         if(!target)
00245                 return;
00246         if(*target == ',')
00247                 target++;                               /* one-letter start */
00248         while (*target && isspace(*target))
00249                 target++;
00250 
00251         stdout = setStdout(cwho);
00252         if(stdout == NULL) {
00253                 PyErr_Print();
00254                 MUXPy_ReportError(who, 1);
00255                 return;
00256         }
00257 
00258         cause = ccause;
00259         who = cwho;
00260         if(cause != prev_cause || who != prev_who)
00261                 update_whocause();
00262 
00263         result = PyRun_String(target, Py_single_input, maindict, maindict);
00264         if(result == NULL) {
00265                 if(PyErr_Occurred()) {
00266                         PyErr_Print();
00267                         PyErr_Clear();
00268                 }
00269         }
00270         if(result != NULL && result != Py_None) {
00271                 PyObject *str = PyObject_Repr(result);
00272 
00273                 if(str != NULL) {
00274                         raw_notify_raw(cwho, PyString_AsString(str), "\r\n");
00275                         Py_DECREF(str);
00276                 }
00277         }
00278 
00279         Py_XDECREF(result);
00280         Py_DECREF(stdout);
00281         cause = prev_cause;
00282         who = prev_who;
00283 }
00284 
00285 void fun_python(char *buff, char **bufc, dbref cwho, dbref ccause,
00286                                 char *fargs[], int nfargs, char *cargs[], int ncargs)
00287 {
00288         char buf[LBUF_SIZE];            /* XXX: Overflow check */
00289         PyObject *stdout, *t;
00290         int eval = 1;
00291         char *c, *target = fargs[0];
00292         dbref prev_cause = cause;
00293         dbref prev_who = who;
00294 
00295         if(MUXPy_State != 1)
00296                 return;
00297         if(!target)
00298                 return;
00299         while (*target && isspace(*target))
00300                 target++;
00301         if(!*target)
00302                 return;
00303 
00304         stdout = setStdout(cwho);
00305         if(stdout == NULL) {
00306                 MUXPy_ReportError(who, 1);
00307                 return;
00308         }
00309 
00310         who = cwho;
00311         cause = ccause;
00312         if(cause != prev_cause || who != prev_who)
00313                 update_whocause();
00314 
00315         if((t = evalString(target))) {
00316                 if(t != Py_None) {
00317                         PyObject *str;
00318 
00319                         if((str = PyObject_Str(t))) {
00320                                 safe_str(PyString_AsString(str), buff, bufc);
00321                                 Py_DECREF(str);
00322                         } else {
00323                                 MUXPy_ReportError(who, 1);
00324                         }
00325                 }
00326         } else {
00327                 MUXPy_ReportError(who, 1);
00328         }
00329         Py_DECREF(stdout);
00330         cause = prev_cause;
00331         who = prev_who;
00332 }
00333 
00334 void fun_pythoncall(char *buff, char **bufc,
00335                                         dbref cwho, dbref ccause,
00336                                         char *fargs[], int nfargs, char *cargs[], int ncargs)
00337 {
00338         char buf[LBUF_SIZE];
00339         char *myfargs[2];
00340         char *to, c;
00341         int i;
00342 
00343         /* Conveniency function that converts arguments and calls fun_python() */
00344 
00345         if(MUXPy_State != 1)
00346                 return;
00347 
00348         if(nfargs < 1) {
00349                 safe_str("#-1 AT LEAST ONE ARGUMENT IS REQUIRED", buff, bufc);
00350                 return;
00351         }
00352 
00353         strcpy(buf, fargs[0]);
00354         to = buf + strlen(buf);
00355         *to++ = '(';
00356         for(i = 1; i < nfargs; i++) {
00357                 char *fr = fargs[i];
00358 
00359                 if(i > 1)
00360                         *to++ = ',';
00361 
00362                 *to++ = '"';
00363                 while (*fr && (to - buf) < (LBUF_SIZE - 10)) {
00364                         c = *fr++;
00365                         if(c == '"')
00366                                 c = '\'';
00367                         *to++ = c;
00368                 }
00369                 if((to - buf) >= (LBUF_SIZE - 10)) {
00370                         safe_str("#-1 ARGUMENTS TOO LONG", buff, bufc);
00371                         return;
00372                 }
00373                 *to++ = '"';
00374         }
00375         *to++ = ')';
00376         *to = 0;
00377         myfargs[0] = buf;
00378         myfargs[1] = NULL;
00379         fun_python(buff, bufc, cwho, ccause, myfargs, 1, cargs, ncargs);
00380 }
00381 
00382 /* Fake stdout, prints to player-dbref. */
00383 
00384 staticforward PyTypeObject PyFakeStdout_Type;
00385 typedef struct {
00386         PyObject_HEAD;
00387         dbref dbref;
00388 } fakestdout;
00389 
00390 static PyObject *fakestdout_write(fakestdout * self, PyObject * args)
00391 {
00392         char *str;
00393 
00394         if(!PyArg_ParseTuple(args, "s:write", &str))
00395                 return NULL;
00396         raw_notify_raw(self->dbref, str, NULL);
00397         Py_INCREF(Py_None);
00398         return Py_None;
00399 }
00400 
00401 static PyObject *MakeFakeStdout(dbref dbref)
00402 {
00403         fakestdout *mo = PyObject_NEW(fakestdout, &PyFakeStdout_Type);
00404 
00405         if(mo == NULL)
00406                 return NULL;
00407         mo->dbref = dbref;
00408         return (PyObject *) mo;
00409 }
00410 
00411 static PyMethodDef fakestdout_methods[] = {
00412         {"write", (PyCFunction) fakestdout_write, METH_VARARGS},
00413         {NULL, NULL}                            /* Sentinel */
00414 };
00415 
00416 static PyObject *fakestdout_getattr(PyObject * self, char *name)
00417 {
00418         return Py_FindMethod(fakestdout_methods, self, name);
00419 }
00420 
00421 static void fakestdout_dealloc(fakestdout * self)
00422 {
00423         PyMem_DEL(self);
00424 }
00425 
00426 static PyTypeObject PyFakeStdout_Type = {
00427 
00428         PyObject_HEAD_INIT(&PyType_Type) 0,     /*ob_size */
00429         "fakestdout",                           /*tp_name */
00430         sizeof(fakestdout),                     /*tp_size */
00431         0,                                                      /*tp_itemsize */
00432         /* methods */
00433         (destructor) fakestdout_dealloc,        /*tp_dealloc */
00434         0,                                                      /*tp_print */
00435         (getattrfunc) fakestdout_getattr,       /*tp_getattr */
00436         0,                                                      /*tp_setattr */
00437         0,                                                      /*tp_compare */
00438         0,                                                      /*tp_repr */
00439 };
00440 
00441 /* MUXObject */
00442 
00443 staticforward PyTypeObject PyMUXObject_Type;
00444 
00445 typedef struct {
00446         PyObject_HEAD;
00447         dbref dbref;
00448 } MUXObject;
00449 
00450 static PyObject *MUXObject_New(dbref dbref)
00451 {
00452         MUXObject *mo;
00453 
00454         mo = PyObject_NEW(MUXObject, &PyMUXObject_Type);
00455         if(mo == NULL)
00456                 return NULL;
00457         mo->dbref = dbref;
00458         return (PyObject *) mo;
00459 }
00460 
00461 static void MUXObject_Del(MUXObject * self)
00462 {
00463         PyMem_DEL(self);
00464 }
00465 
00466 static PyObject *MUXObject_keys(MUXObject * self, PyObject * args)
00467 {
00468         PyObject *p;
00469         int ca;
00470         char *as;
00471         ATTR *attr;
00472 
00473         if(!PyArg_ParseTuple(args, ":keys"))
00474                 return NULL;
00475         p = PyList_New(0);
00476         PyList_Append(p, PyString_FromString("Dbref"));
00477         PyList_Append(p, PyString_FromString("Location"));
00478         for(ca = atr_head(self->dbref, &as); ca; ca = atr_next(&as)) {
00479                 attr = atr_num(ca);
00480                 PyList_Append(p, PyString_FromString(attr->name));
00481         }
00482         return p;
00483 }
00484 
00485 static PyMethodDef MUXObject_methods[] = {
00486         {"keys", (PyCFunction) MUXObject_keys, METH_VARARGS},
00487         {NULL, NULL}                            /* Sentinel */
00488 };
00489 
00490 static PyObject *MUXObject_GetAttr(MUXObject * self, char *name)
00491 {
00492         char *ptr;
00493         int len;
00494         ATTR *a;
00495         char *buf;
00496         int ao, af;                                     /* Attribute owner, attribute flags */
00497         PyObject *p;
00498         PyObject *v = NULL;
00499 
00500         if(strcasecmp(name, "location") == 0)
00501                 return PyInt_FromLong(Location(self->dbref));
00502 
00503         if(strcasecmp(name, "dbref") == 0)
00504                 return PyInt_FromLong(self->dbref);
00505 
00506         if(!(a = atr_str(name)))
00507                 return Py_FindMethod(MUXObject_methods, (PyObject *) self, name);
00508 
00509         buf = alloc_lbuf("python_getattr");     /* XXX: Overflow check */
00510         atr_get_str(buf, self->dbref, a->number, &ao, &af);
00511         p = PyString_FromString(buf);
00512         free_lbuf(buf);
00513         return p;
00514 }
00515 
00516 static int MUXObject_SetAttr(MUXObject * self, char *name, PyObject * v)
00517 {
00518         ATTR *atr;
00519         int attnum;
00520 
00521         atr = atr_str(name);
00522         if(v == NULL) {
00523                 /* delAttr */
00524                 if(atr) {
00525                         atr_clr(self->dbref, atr->number);
00526                         return 0;
00527                 }
00528                 PyErr_SetString(PyExc_AttributeError, "Nonexistent attribute");
00529                 return -1;
00530         }
00531         if(!PyString_Check(v)) {
00532                 PyErr_SetString(PyExc_ValueError,
00533                                                 "Invalid value: only string accepted");
00534                 return -1;
00535         }
00536         attnum = atr ? atr->number : mkattr(name);
00537         atr_add_raw(self->dbref, attnum, PyString_AsString(v));
00538         return 0;
00539 }
00540 
00541 /* Functions in MUX module */
00542 static PyObject *muxc_getobject(PyObject * self, PyObject * args)
00543 {
00544         dbref dbref;
00545 
00546         if(!PyArg_ParseTuple(args, "i:getobject", &dbref))
00547                 return NULL;
00548         return MUXObject_New(dbref);
00549 }
00550 
00551 static PyObject *mux_eval(dbref dbref, char *str)
00552 {
00553         char *buf = alloc_lbuf("objeval");
00554         char *endMarker = buf;
00555         PyObject *rv;
00556 
00557         exec(buf, &endMarker, 0, dbref, cause,
00558                  EV_FCHECK | EV_STRIP | EV_EVAL, &str, NULL, 0);
00559         *endMarker = 0;
00560         if(*buf)
00561                 rv = PyString_FromString(buf);
00562         else {
00563                 Py_INCREF(Py_None);
00564                 rv = Py_None;
00565         }
00566         free_lbuf(buf);
00567         return rv;
00568 }
00569 
00570 static PyObject *muxc_muxeval(PyObject * self, PyObject * args)
00571 {
00572         dbref dbref;
00573         char *str;
00574 
00575         if(!PyArg_ParseTuple(args, "is:muxeval", &dbref, &str))
00576                 return NULL;
00577         return mux_eval(dbref, str);
00578 }
00579 
00580 static PyObject *muxc_notify(PyObject * self, PyObject * args)
00581 {
00582         dbref dbref;
00583         char *str;
00584 
00585         if(!PyArg_ParseTuple(args, "is:notify", &dbref, &str))
00586                 return NULL;
00587         notify(dbref, str);
00588         Py_INCREF(Py_None);
00589         return Py_None;
00590 }
00591 
00592 /* Initialization */ static PyTypeObject PyMUXObject_Type = {
00593         PyObject_HEAD_INIT(&PyType_Type) 0,     /*ob_size */
00594         "MUXobject",                            /*tp_name */
00595         sizeof(MUXObject),                      /*tp_basicsize */
00596         0,                                                      /*tp_itemsize */
00597         /* methods */
00598         (destructor) MUXObject_Del,     /*tp_dealloc */
00599         0,                                                      /*tp_print */
00600         (getattrfunc) MUXObject_GetAttr,        /*tp_getattr */
00601         (setattrfunc) MUXObject_SetAttr,        /*tp_setattr */
00602         0,                                                      /*tp_compare */
00603         0,                                                      /*tp_repr */
00604         0,                                                      /*tp_hash (=as_number) */
00605 };
00606 
00607 static PyMethodDef MUXMethods[] = {
00608         {"getobject", muxc_getobject, METH_VARARGS},
00609         {"muxeval", muxc_muxeval, METH_VARARGS},
00610         {"notify", muxc_notify, METH_VARARGS},
00611         {NULL, NULL}                            /* Sentinel */
00612 };
00613 
00614 static void init_muxmodule()
00615 {
00616         PyImport_AddModule("muxc");
00617         Py_InitModule("muxc", MUXMethods);
00618 }
00619 #endif /* USE_PYTHON */

Generated on Mon May 28 04:25:25 2007 for BattletechMUX by  doxygen 1.4.7