sysvmsg.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. /*
  2. +----------------------------------------------------------------------+
  3. | Copyright (c) The PHP Group |
  4. +----------------------------------------------------------------------+
  5. | This source file is subject to version 3.01 of the PHP license, |
  6. | that is bundled with this package in the file LICENSE, and is |
  7. | available through the world-wide-web at the following url: |
  8. | https://www.php.net/license/3_01.txt |
  9. | If you did not receive a copy of the PHP license and are unable to |
  10. | obtain it through the world-wide-web, please send a note to |
  11. | license@php.net so we can mail you a copy immediately. |
  12. +----------------------------------------------------------------------+
  13. | Author: Wez Furlong <wez@thebrainroom.com> |
  14. +----------------------------------------------------------------------+
  15. */
  16. #ifdef HAVE_CONFIG_H
  17. #include "config.h"
  18. #endif
  19. #include "php.h"
  20. #include "php_globals.h"
  21. #include "ext/standard/info.h"
  22. #include "php_sysvmsg.h"
  23. #include "sysvmsg_arginfo.h"
  24. #include "ext/standard/php_var.h"
  25. #include "zend_smart_str.h"
  26. #include <sys/types.h>
  27. #include <sys/ipc.h>
  28. #include <sys/msg.h>
  29. PHP_MINIT_FUNCTION(sysvmsg);
  30. PHP_MINFO_FUNCTION(sysvmsg);
  31. typedef struct {
  32. key_t key;
  33. zend_long id;
  34. zend_object std;
  35. } sysvmsg_queue_t;
  36. struct php_msgbuf {
  37. zend_long mtype;
  38. char mtext[1];
  39. };
  40. /* In order to detect MSG_EXCEPT use at run time; we have no way
  41. * of knowing what the bit definitions are, so we can't just define
  42. * out own MSG_EXCEPT value. */
  43. #define PHP_MSG_IPC_NOWAIT 1
  44. #define PHP_MSG_NOERROR 2
  45. #define PHP_MSG_EXCEPT 4
  46. /* {{{ sysvmsg_module_entry */
  47. zend_module_entry sysvmsg_module_entry = {
  48. STANDARD_MODULE_HEADER,
  49. "sysvmsg",
  50. ext_functions,
  51. PHP_MINIT(sysvmsg),
  52. NULL,
  53. NULL,
  54. NULL,
  55. PHP_MINFO(sysvmsg),
  56. PHP_SYSVMSG_VERSION,
  57. STANDARD_MODULE_PROPERTIES
  58. };
  59. /* }}} */
  60. #ifdef COMPILE_DL_SYSVMSG
  61. ZEND_GET_MODULE(sysvmsg)
  62. #endif
  63. /* SysvMessageQueue class */
  64. zend_class_entry *sysvmsg_queue_ce;
  65. static zend_object_handlers sysvmsg_queue_object_handlers;
  66. static inline sysvmsg_queue_t *sysvmsg_queue_from_obj(zend_object *obj) {
  67. return (sysvmsg_queue_t *)((char *)(obj) - XtOffsetOf(sysvmsg_queue_t, std));
  68. }
  69. #define Z_SYSVMSG_QUEUE_P(zv) sysvmsg_queue_from_obj(Z_OBJ_P(zv))
  70. static zend_object *sysvmsg_queue_create_object(zend_class_entry *class_type) {
  71. sysvmsg_queue_t *intern = zend_object_alloc(sizeof(sysvmsg_queue_t), class_type);
  72. zend_object_std_init(&intern->std, class_type);
  73. object_properties_init(&intern->std, class_type);
  74. intern->std.handlers = &sysvmsg_queue_object_handlers;
  75. return &intern->std;
  76. }
  77. static zend_function *sysvmsg_queue_get_constructor(zend_object *object) {
  78. zend_throw_error(NULL, "Cannot directly construct SysvMessageQueue, use msg_get_queue() instead");
  79. return NULL;
  80. }
  81. static void sysvmsg_queue_free_obj(zend_object *object)
  82. {
  83. sysvmsg_queue_t *sysvmsg_queue = sysvmsg_queue_from_obj(object);
  84. zend_object_std_dtor(&sysvmsg_queue->std);
  85. }
  86. /* }}} */
  87. /* {{{ PHP_MINIT_FUNCTION */
  88. PHP_MINIT_FUNCTION(sysvmsg)
  89. {
  90. sysvmsg_queue_ce = register_class_SysvMessageQueue();
  91. sysvmsg_queue_ce->create_object = sysvmsg_queue_create_object;
  92. memcpy(&sysvmsg_queue_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
  93. sysvmsg_queue_object_handlers.offset = XtOffsetOf(sysvmsg_queue_t, std);
  94. sysvmsg_queue_object_handlers.free_obj = sysvmsg_queue_free_obj;
  95. sysvmsg_queue_object_handlers.get_constructor = sysvmsg_queue_get_constructor;
  96. sysvmsg_queue_object_handlers.clone_obj = NULL;
  97. sysvmsg_queue_object_handlers.compare = zend_objects_not_comparable;
  98. REGISTER_LONG_CONSTANT("MSG_IPC_NOWAIT", PHP_MSG_IPC_NOWAIT, CONST_PERSISTENT|CONST_CS);
  99. REGISTER_LONG_CONSTANT("MSG_EAGAIN", EAGAIN, CONST_PERSISTENT|CONST_CS);
  100. REGISTER_LONG_CONSTANT("MSG_ENOMSG", ENOMSG, CONST_PERSISTENT|CONST_CS);
  101. REGISTER_LONG_CONSTANT("MSG_NOERROR", PHP_MSG_NOERROR, CONST_PERSISTENT|CONST_CS);
  102. REGISTER_LONG_CONSTANT("MSG_EXCEPT", PHP_MSG_EXCEPT, CONST_PERSISTENT|CONST_CS);
  103. return SUCCESS;
  104. }
  105. /* }}} */
  106. /* {{{ PHP_MINFO_FUNCTION */
  107. PHP_MINFO_FUNCTION(sysvmsg)
  108. {
  109. php_info_print_table_start();
  110. php_info_print_table_row(2, "sysvmsg support", "enabled");
  111. php_info_print_table_end();
  112. }
  113. /* }}} */
  114. /* {{{ Set information for a message queue */
  115. PHP_FUNCTION(msg_set_queue)
  116. {
  117. zval *queue, *data;
  118. sysvmsg_queue_t *mq = NULL;
  119. struct msqid_ds stat;
  120. RETVAL_FALSE;
  121. if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oa", &queue, sysvmsg_queue_ce, &data) == FAILURE) {
  122. RETURN_THROWS();
  123. }
  124. mq = Z_SYSVMSG_QUEUE_P(queue);
  125. if (msgctl(mq->id, IPC_STAT, &stat) == 0) {
  126. zval *item;
  127. /* now pull out members of data and set them in the stat buffer */
  128. if ((item = zend_hash_str_find(Z_ARRVAL_P(data), "msg_perm.uid", sizeof("msg_perm.uid") - 1)) != NULL) {
  129. stat.msg_perm.uid = zval_get_long(item);
  130. }
  131. if ((item = zend_hash_str_find(Z_ARRVAL_P(data), "msg_perm.gid", sizeof("msg_perm.gid") - 1)) != NULL) {
  132. stat.msg_perm.gid = zval_get_long(item);
  133. }
  134. if ((item = zend_hash_str_find(Z_ARRVAL_P(data), "msg_perm.mode", sizeof("msg_perm.mode") - 1)) != NULL) {
  135. stat.msg_perm.mode = zval_get_long(item);
  136. }
  137. if ((item = zend_hash_str_find(Z_ARRVAL_P(data), "msg_qbytes", sizeof("msg_qbytes") - 1)) != NULL) {
  138. stat.msg_qbytes = zval_get_long(item);
  139. }
  140. if (msgctl(mq->id, IPC_SET, &stat) == 0) {
  141. RETVAL_TRUE;
  142. }
  143. }
  144. }
  145. /* }}} */
  146. /* {{{ Returns information about a message queue */
  147. PHP_FUNCTION(msg_stat_queue)
  148. {
  149. zval *queue;
  150. sysvmsg_queue_t *mq = NULL;
  151. struct msqid_ds stat;
  152. RETVAL_FALSE;
  153. if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &queue, sysvmsg_queue_ce) == FAILURE) {
  154. RETURN_THROWS();
  155. }
  156. mq = Z_SYSVMSG_QUEUE_P(queue);
  157. if (msgctl(mq->id, IPC_STAT, &stat) == 0) {
  158. array_init(return_value);
  159. add_assoc_long(return_value, "msg_perm.uid", stat.msg_perm.uid);
  160. add_assoc_long(return_value, "msg_perm.gid", stat.msg_perm.gid);
  161. add_assoc_long(return_value, "msg_perm.mode", stat.msg_perm.mode);
  162. add_assoc_long(return_value, "msg_stime", stat.msg_stime);
  163. add_assoc_long(return_value, "msg_rtime", stat.msg_rtime);
  164. add_assoc_long(return_value, "msg_ctime", stat.msg_ctime);
  165. add_assoc_long(return_value, "msg_qnum", stat.msg_qnum);
  166. add_assoc_long(return_value, "msg_qbytes", stat.msg_qbytes);
  167. add_assoc_long(return_value, "msg_lspid", stat.msg_lspid);
  168. add_assoc_long(return_value, "msg_lrpid", stat.msg_lrpid);
  169. }
  170. }
  171. /* }}} */
  172. /* {{{ Check whether a message queue exists */
  173. PHP_FUNCTION(msg_queue_exists)
  174. {
  175. zend_long key;
  176. if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &key) == FAILURE) {
  177. RETURN_THROWS();
  178. }
  179. if (msgget(key, 0) < 0) {
  180. RETURN_FALSE;
  181. }
  182. RETURN_TRUE;
  183. }
  184. /* }}} */
  185. /* {{{ Attach to a message queue */
  186. PHP_FUNCTION(msg_get_queue)
  187. {
  188. zend_long key;
  189. zend_long perms = 0666;
  190. sysvmsg_queue_t *mq;
  191. if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|l", &key, &perms) == FAILURE) {
  192. RETURN_THROWS();
  193. }
  194. object_init_ex(return_value, sysvmsg_queue_ce);
  195. mq = Z_SYSVMSG_QUEUE_P(return_value);
  196. mq->key = key;
  197. mq->id = msgget(key, 0);
  198. if (mq->id < 0) {
  199. /* doesn't already exist; create it */
  200. mq->id = msgget(key, IPC_CREAT | IPC_EXCL | perms);
  201. if (mq->id < 0) {
  202. php_error_docref(NULL, E_WARNING, "Failed for key 0x" ZEND_XLONG_FMT ": %s", key, strerror(errno));
  203. zval_ptr_dtor(return_value);
  204. RETURN_FALSE;
  205. }
  206. }
  207. }
  208. /* }}} */
  209. /* {{{ Destroy the queue */
  210. PHP_FUNCTION(msg_remove_queue)
  211. {
  212. zval *queue;
  213. sysvmsg_queue_t *mq = NULL;
  214. if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &queue, sysvmsg_queue_ce) == FAILURE) {
  215. RETURN_THROWS();
  216. }
  217. mq = Z_SYSVMSG_QUEUE_P(queue);
  218. if (msgctl(mq->id, IPC_RMID, NULL) == 0) {
  219. RETVAL_TRUE;
  220. } else {
  221. RETVAL_FALSE;
  222. }
  223. }
  224. /* }}} */
  225. /* {{{ Send a message of type msgtype (must be > 0) to a message queue */
  226. PHP_FUNCTION(msg_receive)
  227. {
  228. zval *out_message, *queue, *out_msgtype, *zerrcode = NULL;
  229. zend_long desiredmsgtype, maxsize, flags = 0;
  230. zend_long realflags = 0;
  231. bool do_unserialize = 1;
  232. sysvmsg_queue_t *mq = NULL;
  233. struct php_msgbuf *messagebuffer = NULL; /* buffer to transmit */
  234. int result;
  235. RETVAL_FALSE;
  236. if (zend_parse_parameters(ZEND_NUM_ARGS(), "Olzlz|blz",
  237. &queue, sysvmsg_queue_ce, &desiredmsgtype, &out_msgtype, &maxsize,
  238. &out_message, &do_unserialize, &flags, &zerrcode) == FAILURE) {
  239. RETURN_THROWS();
  240. }
  241. if (maxsize <= 0) {
  242. zend_argument_value_error(4, "must be greater than 0");
  243. RETURN_THROWS();
  244. }
  245. if (flags != 0) {
  246. if (flags & PHP_MSG_EXCEPT) {
  247. #ifndef MSG_EXCEPT
  248. php_error_docref(NULL, E_WARNING, "MSG_EXCEPT is not supported on your system");
  249. RETURN_FALSE;
  250. #else
  251. realflags |= MSG_EXCEPT;
  252. #endif
  253. }
  254. if (flags & PHP_MSG_NOERROR) {
  255. realflags |= MSG_NOERROR;
  256. }
  257. if (flags & PHP_MSG_IPC_NOWAIT) {
  258. realflags |= IPC_NOWAIT;
  259. }
  260. }
  261. mq = Z_SYSVMSG_QUEUE_P(queue);
  262. messagebuffer = (struct php_msgbuf *) safe_emalloc(maxsize, 1, sizeof(struct php_msgbuf));
  263. result = msgrcv(mq->id, messagebuffer, maxsize, desiredmsgtype, realflags);
  264. if (result >= 0) {
  265. /* got it! */
  266. ZEND_TRY_ASSIGN_REF_LONG(out_msgtype, messagebuffer->mtype);
  267. if (zerrcode) {
  268. ZEND_TRY_ASSIGN_REF_LONG(zerrcode, 0);
  269. }
  270. RETVAL_TRUE;
  271. if (do_unserialize) {
  272. php_unserialize_data_t var_hash;
  273. zval tmp;
  274. const unsigned char *p = (const unsigned char *) messagebuffer->mtext;
  275. PHP_VAR_UNSERIALIZE_INIT(var_hash);
  276. if (!php_var_unserialize(&tmp, &p, p + result, &var_hash)) {
  277. php_error_docref(NULL, E_WARNING, "Message corrupted");
  278. ZEND_TRY_ASSIGN_REF_FALSE(out_message);
  279. RETVAL_FALSE;
  280. } else {
  281. ZEND_TRY_ASSIGN_REF_TMP(out_message, &tmp);
  282. }
  283. PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
  284. } else {
  285. ZEND_TRY_ASSIGN_REF_STRINGL(out_message, messagebuffer->mtext, result);
  286. }
  287. } else {
  288. ZEND_TRY_ASSIGN_REF_LONG(out_msgtype, 0);
  289. ZEND_TRY_ASSIGN_REF_FALSE(out_message);
  290. if (zerrcode) {
  291. ZEND_TRY_ASSIGN_REF_LONG(zerrcode, errno);
  292. }
  293. }
  294. efree(messagebuffer);
  295. }
  296. /* }}} */
  297. /* {{{ Send a message of type msgtype (must be > 0) to a message queue */
  298. PHP_FUNCTION(msg_send)
  299. {
  300. zval *message, *queue, *zerror=NULL;
  301. zend_long msgtype;
  302. bool do_serialize = 1, blocking = 1;
  303. sysvmsg_queue_t * mq = NULL;
  304. struct php_msgbuf * messagebuffer = NULL; /* buffer to transmit */
  305. int result;
  306. size_t message_len = 0;
  307. RETVAL_FALSE;
  308. if (zend_parse_parameters(ZEND_NUM_ARGS(), "Olz|bbz",
  309. &queue, sysvmsg_queue_ce, &msgtype, &message, &do_serialize, &blocking, &zerror) == FAILURE) {
  310. RETURN_THROWS();
  311. }
  312. mq = Z_SYSVMSG_QUEUE_P(queue);
  313. if (do_serialize) {
  314. smart_str msg_var = {0};
  315. php_serialize_data_t var_hash;
  316. PHP_VAR_SERIALIZE_INIT(var_hash);
  317. php_var_serialize(&msg_var, message, &var_hash);
  318. PHP_VAR_SERIALIZE_DESTROY(var_hash);
  319. /* NB: php_msgbuf is 1 char bigger than a long, so there is no need to
  320. * allocate the extra byte. */
  321. messagebuffer = safe_emalloc(ZSTR_LEN(msg_var.s), 1, sizeof(struct php_msgbuf));
  322. memcpy(messagebuffer->mtext, ZSTR_VAL(msg_var.s), ZSTR_LEN(msg_var.s) + 1);
  323. message_len = ZSTR_LEN(msg_var.s);
  324. smart_str_free(&msg_var);
  325. } else {
  326. char *p;
  327. switch (Z_TYPE_P(message)) {
  328. case IS_STRING:
  329. p = Z_STRVAL_P(message);
  330. message_len = Z_STRLEN_P(message);
  331. break;
  332. case IS_LONG:
  333. message_len = spprintf(&p, 0, ZEND_LONG_FMT, Z_LVAL_P(message));
  334. break;
  335. case IS_FALSE:
  336. message_len = spprintf(&p, 0, "0");
  337. break;
  338. case IS_TRUE:
  339. message_len = spprintf(&p, 0, "1");
  340. break;
  341. case IS_DOUBLE:
  342. message_len = spprintf(&p, 0, "%F", Z_DVAL_P(message));
  343. break;
  344. default:
  345. zend_argument_type_error(3, "must be of type string|int|float|bool, %s given", zend_zval_type_name(message));
  346. RETURN_THROWS();
  347. }
  348. messagebuffer = safe_emalloc(message_len, 1, sizeof(struct php_msgbuf));
  349. memcpy(messagebuffer->mtext, p, message_len + 1);
  350. if (Z_TYPE_P(message) != IS_STRING) {
  351. efree(p);
  352. }
  353. }
  354. /* set the message type */
  355. messagebuffer->mtype = msgtype;
  356. result = msgsnd(mq->id, messagebuffer, message_len, blocking ? 0 : IPC_NOWAIT);
  357. efree(messagebuffer);
  358. if (result == -1) {
  359. php_error_docref(NULL, E_WARNING, "msgsnd failed: %s", strerror(errno));
  360. if (zerror) {
  361. ZEND_TRY_ASSIGN_REF_LONG(zerror, errno);
  362. }
  363. } else {
  364. RETVAL_TRUE;
  365. }
  366. }
  367. /* }}} */