00001
00002
00003
00004
00005
00006
00007
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
00032
00033
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
00043 if(clearerr)
00044 PyErr_Clear();
00045 }
00046
00047 void MUXPy_Abort(char *message)
00048 {
00049
00050
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
00158 MUXPy_Abort("Can't create empty tuple");
00159 return;
00160 }
00161 result = PyEval_CallObject(hookobj, arglist);
00162
00163 } else {
00164 MUXPy_ReportError(-1, 1);
00165 }
00166 } else {
00167
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
00184
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
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++;
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];
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
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
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}
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,
00429 "fakestdout",
00430 sizeof(fakestdout),
00431 0,
00432
00433 (destructor) fakestdout_dealloc,
00434 0,
00435 (getattrfunc) fakestdout_getattr,
00436 0,
00437 0,
00438 0,
00439 };
00440
00441
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}
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;
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");
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
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
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 static PyTypeObject PyMUXObject_Type = {
00593 PyObject_HEAD_INIT(&PyType_Type) 0,
00594 "MUXobject",
00595 sizeof(MUXObject),
00596 0,
00597
00598 (destructor) MUXObject_Del,
00599 0,
00600 (getattrfunc) MUXObject_GetAttr,
00601 (setattrfunc) MUXObject_SetAttr,
00602 0,
00603 0,
00604 0,
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}
00612 };
00613
00614 static void init_muxmodule()
00615 {
00616 PyImport_AddModule("muxc");
00617 Py_InitModule("muxc", MUXMethods);
00618 }
00619 #endif