FileShare.C 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575
  1. /*
  2. * CDE - Common Desktop Environment
  3. *
  4. * Copyright (c) 1993-2012, The Open Group. All rights reserved.
  5. *
  6. * These libraries and programs are free software; you can
  7. * redistribute them and/or modify them under the terms of the GNU
  8. * Lesser General Public License as published by the Free Software
  9. * Foundation; either version 2 of the License, or (at your option)
  10. * any later version.
  11. *
  12. * These libraries and programs are distributed in the hope that
  13. * they will be useful, but WITHOUT ANY WARRANTY; without even the
  14. * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  15. * PURPOSE. See the GNU Lesser General Public License for more
  16. * details.
  17. *
  18. * You should have received a copy of the GNU Lesser General Public
  19. * License along with these libraries and programs; if not, write
  20. * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
  21. * Floor, Boston, MA 02110-1301 USA
  22. */
  23. /*
  24. *+SNOTICE
  25. *
  26. *
  27. * $TOG: FileShare.C /main/6 1999/03/26 16:52:00 mgreess $
  28. *
  29. * RESTRICTED CONFIDENTIAL INFORMATION:
  30. *
  31. * The information in this document is subject to special
  32. * restrictions in a confidential disclosure agreement bertween
  33. * HP, IBM, Sun, USL, SCO and Univel. Do not distribute this
  34. * document outside HP, IBM, Sun, USL, SCO, or Univel wihtout
  35. * Sun's specific written approval. This documment and all copies
  36. * and derivative works thereof must be returned or destroyed at
  37. * Sun's request.
  38. *
  39. * Copyright 1993 Sun Microsystems, Inc. All rights reserved.
  40. *
  41. *+ENOTICE
  42. */
  43. #include <stdio.h>
  44. #include <stdlib.h>
  45. #include <string.h>
  46. #include <unistd.h>
  47. #include <fcntl.h>
  48. #include <DtMail/FileShare.hh>
  49. #include <DtMail/DtMailXtProc.h>
  50. XtAppContext DtMailDamageContext = NULL;
  51. static const int FileShareTimeout = 900000;
  52. static int tlock_flag = 1;
  53. static Tt_message msg_create(char *op, char *file, Tt_class tt_class, Tt_message_callback f)
  54. {
  55. Tt_message msg = tt_message_create();
  56. // Create the tooltalk message
  57. if (tt_ptr_error(msg) != TT_OK) {
  58. return ((Tt_message) NULL);
  59. }
  60. /* Set the message class type */
  61. if (tt_message_class_set (msg, tt_class) != TT_OK) {
  62. tt_message_destroy(msg);
  63. return ((Tt_message) NULL);
  64. }
  65. /* Set the message address */
  66. if (tt_message_address_set (msg, TT_PROCEDURE) != TT_OK) {
  67. tt_message_destroy(msg);
  68. return ((Tt_message) NULL);
  69. }
  70. /* Set the disposition of the message */
  71. if (tt_message_disposition_set (msg, TT_DISCARD) != TT_OK) {
  72. tt_message_destroy(msg);
  73. return ((Tt_message) NULL);
  74. }
  75. /* Set the message operation. */
  76. if (tt_message_op_set (msg, op) != TT_OK) {
  77. tt_message_destroy(msg);
  78. return ((Tt_message) NULL);
  79. }
  80. /* Set the message scope */
  81. if (tt_message_scope_set (msg, TT_FILE) != TT_OK) {
  82. tt_message_destroy(msg);
  83. return ((Tt_message) NULL);
  84. }
  85. if (tt_message_file_set (msg, file) != TT_OK) {
  86. tt_message_destroy(msg);
  87. return ((Tt_message) NULL);
  88. }
  89. if (tt_message_arg_add(msg, TT_IN, "DtMail", "lock") != TT_OK) {
  90. tt_message_destroy(msg);
  91. return ((Tt_message) NULL);
  92. }
  93. if (f) {
  94. if (tt_message_callback_add(msg, f) != TT_OK) {
  95. tt_message_destroy(msg);
  96. return ((Tt_message) NULL);
  97. }
  98. }
  99. return (msg);
  100. }
  101. Tt_callback_action
  102. FileShare::mt_lock_cb(Tt_message m, Tt_pattern p)
  103. {
  104. Tt_state state = tt_message_state(m);
  105. char *op;
  106. char *flag = NULL;
  107. op = tt_message_op(m);
  108. if (!op) {
  109. return TT_CALLBACK_CONTINUE;
  110. }
  111. flag = tt_message_arg_val(m, 0);
  112. if (!strcmp(op, "tlock")) {
  113. // handle tlock request
  114. if (state == TT_HANDLED) {
  115. tlock_flag = -1;
  116. tt_message_destroy(m);
  117. } else if (state == TT_FAILED) {
  118. tlock_flag = 0;
  119. tt_message_destroy(m);
  120. } else if (state == TT_SENT) {
  121. if (tt_ptr_error(flag) == TT_OK) {
  122. // this message is from another dtmail, ignore it
  123. return TT_CALLBACK_CONTINUE;
  124. }
  125. tt_message_reply(m);
  126. }
  127. } else if (!strcmp(op, "rulock")) {
  128. // handle rulock notice
  129. if (state == TT_SENT) {
  130. DtMailBoolean answer = DTM_FALSE; // default is to not give up the lock
  131. FileShare *f = (FileShare *)tt_pattern_user(p, 1);
  132. if (f->_cb_func)
  133. {
  134. char *msg =
  135. DtMailEnv::getMessageText(
  136. FileShareMsgSet, 5,
  137. "Another user would like your lock.");
  138. answer = f->_cb_func(DTMC_UNLOCK, f->_path, msg, f->_cb_data);
  139. }
  140. tt_message_destroy(m);
  141. }
  142. }
  143. tt_free(op);
  144. return TT_CALLBACK_PROCESSED;
  145. }
  146. FileShare::FileShare(DtMailEnv & error,
  147. DtMail::Session * session,
  148. const char * path,
  149. DtMailCallback cb,
  150. void * clientData)
  151. {
  152. DtMail::MailRc *mailrc = session->mailRc(error);
  153. _key = session->newObjectKey();
  154. _session = session;
  155. _path = strdup(path);
  156. _cb_func = cb;
  157. _cb_data = clientData;
  158. // For now, assume we can't write to the file.
  159. //
  160. _have_write_access = DTM_FALSE;
  161. _other_modified = DTM_TRUE;
  162. _mt_pattern = NULL;
  163. error.clear();
  164. // Register the file pattern.
  165. //
  166. _tt_handle = new TTHandle;
  167. _tt_handle->session = _session;
  168. _tt_handle->key = _key;
  169. _tt_handle->self = this;
  170. _file_pats = ttdt_file_join(_path, TT_FILE, 0, fileCB, _tt_handle);
  171. if (tt_pointer_error(_file_pats) != TT_OK) {
  172. error.setError(DTME_TTFailure);
  173. _file_pats = NULL;
  174. return;
  175. }
  176. // isModified(error);
  177. }
  178. FileShare::~FileShare(void)
  179. {
  180. if (_have_write_access == DTM_TRUE && _file_pats) {
  181. _pending = PENDING_DESTROY;
  182. // ttdt_file_event(NULL, TTDT_SAVED, _file_pats, 1);
  183. _session->removeObjectKey(_key);
  184. }
  185. if (NULL != _tt_handle)
  186. delete _tt_handle;
  187. if (_file_pats) {
  188. ttdt_file_quit(_file_pats, 1);
  189. }
  190. if (_mt_pattern) {
  191. tt_pattern_destroy(_mt_pattern);
  192. _mt_pattern = NULL;
  193. }
  194. if (_path) {
  195. free(_path);
  196. }
  197. _have_write_access = DTM_FALSE;
  198. _file_pats = NULL;
  199. }
  200. DtMailBoolean
  201. FileShare::isModified(DtMailEnv & error)
  202. {
  203. error.clear();
  204. if (!_path) {
  205. return(DTM_FALSE);
  206. }
  207. DtMailBoolean answer = DTM_FALSE;
  208. Tt_message mt_msg;
  209. mt_msg = msg_create("tlock", _path, TT_REQUEST, mt_lock_cb);
  210. if (mt_msg == NULL) {
  211. error.setError(DTME_TTFailure);
  212. return DTM_TRUE;
  213. }
  214. if (tt_message_send(mt_msg) != TT_OK) {
  215. error.setError(DTME_TTFailure);
  216. return DTM_TRUE;
  217. }
  218. tttk_block_while((XtAppContext)0, &tlock_flag, FileShareTimeout);
  219. // mt_lock_cb sets tlock_flag to -1 if mbox is locked
  220. if (tlock_flag == -1) {
  221. tlock_flag = 1; // reset the tlock_flag
  222. _other_modified = DTM_TRUE;
  223. _mt_lock = DTM_TRUE;
  224. return DTM_TRUE;
  225. } else {
  226. // else tlock_flag == 0, means no lock on this mbox
  227. // or tlock_flag == 1, means time out
  228. tlock_flag = 1; // reset the tlock_flag
  229. _mt_lock = DTM_FALSE;
  230. // now let's try the dtmail protocol
  231. if (ttdt_Get_Modified(NULL, _path, TT_FILE, NULL, FileShareTimeout)) {
  232. answer = DTM_TRUE;
  233. _other_modified = DTM_TRUE;
  234. } else {
  235. answer = DTM_FALSE;
  236. _other_modified = DTM_FALSE;
  237. }
  238. return(answer);
  239. }
  240. }
  241. void
  242. FileShare::lockFile(DtMailEnv & error)
  243. {
  244. int always;
  245. error.clear();
  246. // If we have the access, then we locked it before. Simply return.
  247. //
  248. if (_have_write_access == DTM_TRUE) {
  249. return;
  250. }
  251. // First step in locking is determining if anyone else has the lock.
  252. // If they do, then we need to ask them to save their changes and
  253. // exit.
  254. //
  255. if (isModified(error) == DTM_TRUE) {
  256. DtMailBoolean take_lock = DTM_FALSE; // default is to not request access
  257. // calls syncViewAndStoreCallback which then calls syncViewAndStore
  258. if (_cb_func)
  259. {
  260. char *msg =
  261. DtMailEnv::getMessageText(
  262. FileShareMsgSet, 6,
  263. "Another session has this mailbox locked. Request access?");
  264. take_lock = _cb_func(DTMC_QUERYLOCK, _path, msg, _cb_data);
  265. }
  266. if (take_lock == DTM_FALSE) {
  267. error.setError(DTME_OtherOwnsWrite);
  268. return;
  269. }
  270. // isModified sets _mt_lock to DTM_TRUE is the mailbox is locked
  271. // by mailtool
  272. if (_mt_lock == DTM_TRUE) {
  273. // mailtool style locking
  274. Tt_message mt_msg;
  275. mt_msg = msg_create("rulock", _path, TT_NOTICE, NULL);
  276. if (mt_msg == NULL) {
  277. error.setError(DTME_TTFailure);
  278. return;
  279. }
  280. if (tt_message_send(mt_msg) != TT_OK) {
  281. error.setError(DTME_TTFailure);
  282. return;
  283. }
  284. tt_message_destroy(mt_msg);
  285. } else {
  286. // ttdt style locking
  287. ttdt_Save(NULL, _path, TT_FILE, DtMailDamageContext, FileShareTimeout);
  288. }
  289. // Give the other mailer FileShareTimeout seconds to give up the lock
  290. time_t t_start;
  291. time(&t_start);
  292. while (1) {
  293. sleep(5);
  294. if (isModified(error) == DTM_FALSE) {
  295. break;
  296. } else {
  297. if (time(NULL) - t_start > FileShareTimeout) {
  298. // time out!
  299. error.setError(DTME_OtherOwnsWrite);
  300. return;
  301. }
  302. }
  303. }
  304. }
  305. // Set this so we don't call our client during this handshake.
  306. //
  307. _pending = PENDING_LOCK;
  308. _outstanding = DTM_TRUE;
  309. // Now we are ready to lock the mailbox
  310. // register this pattern so we can handle messages from mailtool
  311. _mt_pattern = tt_pattern_create();
  312. tt_pattern_category_set(_mt_pattern, TT_HANDLE);
  313. tt_pattern_scope_add(_mt_pattern, TT_FILE);
  314. tt_pattern_file_add(_mt_pattern, _path);
  315. tt_pattern_op_add(_mt_pattern, "tlock");
  316. tt_pattern_op_add(_mt_pattern, "rulock");
  317. tt_pattern_callback_add(_mt_pattern, mt_lock_cb);
  318. tt_pattern_user_set(_mt_pattern, 1, (void *)this);
  319. if (tt_pattern_register(_mt_pattern) != TT_OK) {
  320. error.setError(DTME_TTFailure);
  321. return;
  322. }
  323. // Send the message saying we want to be the owner.
  324. ttdt_file_event(NULL, TTDT_MODIFIED, _file_pats, 1);
  325. // We need to process any messages that have arrived. We will get our own
  326. // modified message, which is not terribly interesting. What is interesting
  327. // is a modified message from someone else. That means that we have a race
  328. // condition where two processes both asked if the file was being modified,
  329. // and it wasn't. Then both said they were the owner, which is obviously
  330. // wrong so we need to blow both off and make them try again. Hopefully
  331. // there is enough randomness in our clients that the race condition will
  332. // clear itself up and we won't get here very often.
  333. //
  334. always = 1;
  335. while(_outstanding == DTM_TRUE) {
  336. tttk_block_while((XtAppContext)0, &always, 0);
  337. }
  338. if (_other_modified == DTM_TRUE) {
  339. // Well, we have a race. Fail this lock as will the other process,
  340. // we hope.
  341. error.setError(DTME_OtherOwnsWrite);
  342. return;
  343. }
  344. // Okay, we now have the lock.
  345. _have_write_access = DTM_TRUE;
  346. }
  347. DtMailBoolean
  348. FileShare::readOnly(DtMailEnv & error)
  349. {
  350. DtMailBoolean answer = DTM_TRUE; // default is to accept read-only access
  351. if (_cb_func)
  352. {
  353. char *msg =
  354. DtMailEnv::getMessageText(
  355. FileShareMsgSet, 7,
  356. "Unable to obtain lock, open this mailbox as read only?");
  357. answer = _cb_func(DTMC_READONLY, _path, msg, _cb_data);
  358. }
  359. if (answer)
  360. error.clear();
  361. return(answer);
  362. }
  363. DtMailBoolean
  364. FileShare::readWriteOverride(DtMailEnv & error)
  365. {
  366. DtMailBoolean answer = DTM_FALSE; // default is to open for read-only access
  367. if (_cb_func)
  368. {
  369. char *msg =
  370. DtMailEnv::getMessageText(
  371. FileShareMsgSet, 8,
  372. "Unable to obtain lock because system not responding, open this mailbox as read only, read write, or cancel?");
  373. answer = _cb_func(DTMC_READWRITEOVERRIDE, _path, msg, _cb_data);
  374. }
  375. if (answer == ((DtMailBoolean)((DTM_FALSE+DTM_TRUE)*2))) {
  376. error.setError(DTME_UserInterrupted);
  377. answer = DTM_FALSE;
  378. }
  379. else {
  380. error.clear();
  381. }
  382. return(answer);
  383. }
  384. #ifdef DEAD_WOOD
  385. DtMailBoolean
  386. FileShare::locked(void)
  387. {
  388. return(_have_write_access);
  389. }
  390. #endif /* DEAD_WOOD */
  391. Tt_message
  392. FileShare::fileCB(Tt_message msg,
  393. Tttk_op op,
  394. char * path,
  395. void *clientData,
  396. int,
  397. int same_proc)
  398. {
  399. TTHandle *tt_handle = (TTHandle *)clientData;
  400. DtMailBoolean answer;
  401. if (tt_handle->session->validObjectKey(tt_handle->key) == DTM_FALSE) {
  402. // This object has been destroyed. We got here most likely because
  403. // ToolTalk is responding to one of our clean up messages. In any
  404. // case, fail the message and return.
  405. //
  406. tttk_message_fail(msg, TT_DESKTOP_ECANCELED, "Object destroyed", 1);
  407. return(0);
  408. }
  409. FileShare * self = tt_handle->self;
  410. switch(op) {
  411. case TTDT_MODIFIED:
  412. if (self->_outstanding == DTM_FALSE && !same_proc) {
  413. if (self->_cb_func)
  414. {
  415. char *msg =
  416. DtMailEnv::getMessageText(
  417. FileShareMsgSet, 9,
  418. "Another user has taken your lock.");
  419. self->_cb_func(DTMC_LOSTLOCK, path, msg, self->_cb_data);
  420. }
  421. self->_other_modified = DTM_TRUE;
  422. self->_have_write_access = DTM_FALSE;
  423. break;
  424. }
  425. if (self->_outstanding == DTM_TRUE && self->_pending == PENDING_LOCK) {
  426. // This could be one of 2 conditions. If the message is
  427. // from us, then we have the lock, and we are done.
  428. // If not, then someone else is asking for the lock. We
  429. // reflect this by giving them the lock.
  430. //
  431. if (same_proc) {
  432. self->_other_modified = DTM_FALSE;
  433. self->_have_write_access = DTM_TRUE;
  434. self->_outstanding = DTM_FALSE;
  435. }
  436. else {
  437. self->_other_modified = DTM_TRUE;
  438. self->_have_write_access = DTM_FALSE;
  439. // We haven't seen our own request yet. Leave outstanding
  440. // so we can process it before leaving.
  441. }
  442. }
  443. break;
  444. case TTDT_GET_MODIFIED:
  445. tt_message_arg_ival_set(msg, 1, 1);
  446. tt_message_reply(msg);
  447. break;
  448. case TTDT_SAVED:
  449. case TTDT_REVERTED:
  450. // The other process has saved their changes (or tossed them).
  451. // At this point we should be able to start modifying the file.
  452. //
  453. self->_other_modified = DTM_FALSE;
  454. if (self->_outstanding == DTM_TRUE && self->_pending == PENDING_SAVE) {
  455. self->_outstanding = DTM_FALSE;
  456. }
  457. break;
  458. case TTDT_REVERT:
  459. case TTDT_SAVE:
  460. // Someone is asking us to save our changes and close the file.
  461. //
  462. answer = DTM_FALSE; // default is to not give up the lock
  463. if (self->_cb_func)
  464. {
  465. char *msg =
  466. DtMailEnv::getMessageText(
  467. FileShareMsgSet, 5,
  468. "Another user would like your lock.");
  469. answer = self->_cb_func(DTMC_UNLOCK, path, msg, self->_cb_data);
  470. }
  471. if (answer == DTM_TRUE) {
  472. tt_message_reply(msg);
  473. } else {
  474. tttk_message_fail(msg, TT_DESKTOP_EACCES, 0, 0);
  475. }
  476. break;
  477. default:
  478. // Other messages, we simply smile and say thank you.:-)
  479. //
  480. tt_message_reply(msg);
  481. break;
  482. }
  483. tt_message_destroy(msg);
  484. return(0);
  485. }