2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
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)
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
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these librararies and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
27 * $TOG: FileShare.C /main/6 1999/03/26 16:52:00 mgreess $
29 * RESTRICTED CONFIDENTIAL INFORMATION:
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
39 * Copyright 1993 Sun Microsystems, Inc. All rights reserved.
50 #include <DtMail/FileShare.hh>
51 #include <DtMail/DtMailXtProc.h>
53 XtAppContext DtMailDamageContext = NULL;
55 static const int FileShareTimeout = 900000;
56 static int tlock_flag = 1;
59 static Tt_message msg_create(char *op, char *file, Tt_class tt_class, Tt_message_callback f)
61 Tt_message msg = tt_message_create();
63 // Create the tooltalk message
64 if (tt_ptr_error(msg) != TT_OK) {
65 return ((Tt_message) NULL);
68 /* Set the message class type */
69 if (tt_message_class_set (msg, tt_class) != TT_OK) {
70 tt_message_destroy(msg);
71 return ((Tt_message) NULL);
74 /* Set the message address */
75 if (tt_message_address_set (msg, TT_PROCEDURE) != TT_OK) {
76 tt_message_destroy(msg);
77 return ((Tt_message) NULL);
80 /* Set the disposition of the message */
81 if (tt_message_disposition_set (msg, TT_DISCARD) != TT_OK) {
82 tt_message_destroy(msg);
83 return ((Tt_message) NULL);
86 /* Set the message operation. */
87 if (tt_message_op_set (msg, op) != TT_OK) {
88 tt_message_destroy(msg);
89 return ((Tt_message) NULL);
92 /* Set the message scope */
93 if (tt_message_scope_set (msg, TT_FILE) != TT_OK) {
94 tt_message_destroy(msg);
95 return ((Tt_message) NULL);
98 if (tt_message_file_set (msg, file) != TT_OK) {
99 tt_message_destroy(msg);
100 return ((Tt_message) NULL);
103 if (tt_message_arg_add(msg, TT_IN, "DtMail", "lock") != TT_OK) {
104 tt_message_destroy(msg);
105 return ((Tt_message) NULL);
109 if (tt_message_callback_add(msg, f) != TT_OK) {
110 tt_message_destroy(msg);
111 return ((Tt_message) NULL);
119 FileShare::mt_lock_cb(Tt_message m, Tt_pattern p)
121 Tt_state state = tt_message_state(m);
125 op = tt_message_op(m);
127 return TT_CALLBACK_CONTINUE;
130 flag = tt_message_arg_val(m, 0);
132 if (!strcmp(op, "tlock")) {
133 // handle tlock request
134 if (state == TT_HANDLED) {
136 tt_message_destroy(m);
137 } else if (state == TT_FAILED) {
139 tt_message_destroy(m);
140 } else if (state == TT_SENT) {
141 if (tt_ptr_error(flag) == TT_OK) {
142 // this message is from another dtmail, ignore it
143 return TT_CALLBACK_CONTINUE;
148 } else if (!strcmp(op, "rulock")) {
149 // handle rulock notice
150 if (state == TT_SENT) {
151 DtMailBoolean answer = DTM_FALSE; // default is to not give up the lock
152 FileShare *f = (FileShare *)tt_pattern_user(p, 1);
156 DtMailEnv::getMessageText(
158 "Another user would like your lock.");
159 answer = f->_cb_func(DTMC_UNLOCK, f->_path, msg, f->_cb_data);
161 tt_message_destroy(m);
166 return TT_CALLBACK_PROCESSED;
169 FileShare::FileShare(DtMailEnv & error,
170 DtMail::Session * session,
175 DtMail::MailRc *mailrc = session->mailRc(error);
177 _key = session->newObjectKey();
179 _path = strdup(path);
181 _cb_data = clientData;
183 // For now, assume we can't write to the file.
185 _have_write_access = DTM_FALSE;
186 _other_modified = DTM_TRUE;
190 // Register the file pattern.
192 _tt_handle = new TTHandle;
193 _tt_handle->session = _session;
194 _tt_handle->key = _key;
195 _tt_handle->self = this;
197 _file_pats = ttdt_file_join(_path, TT_FILE, 0, fileCB, _tt_handle);
198 if (tt_pointer_error(_file_pats) != TT_OK) {
199 error.setError(DTME_TTFailure);
204 // isModified(error);
207 FileShare::~FileShare(void)
210 if (_have_write_access == DTM_TRUE && _file_pats) {
211 _pending = PENDING_DESTROY;
213 // ttdt_file_event(NULL, TTDT_SAVED, _file_pats, 1);
215 _session->removeObjectKey(_key);
218 if (NULL != _tt_handle)
222 ttdt_file_quit(_file_pats, 1);
226 tt_pattern_destroy(_mt_pattern);
234 _have_write_access = DTM_FALSE;
239 FileShare::isModified(DtMailEnv & error)
247 DtMailBoolean answer = DTM_FALSE;
251 mt_msg = msg_create("tlock", _path, TT_REQUEST, mt_lock_cb);
252 if (mt_msg == NULL) {
253 error.setError(DTME_TTFailure);
257 if (tt_message_send(mt_msg) != TT_OK) {
258 error.setError(DTME_TTFailure);
262 tttk_block_while((XtAppContext)0, &tlock_flag, FileShareTimeout);
264 // mt_lock_cb sets tlock_flag to -1 if mbox is locked
265 if (tlock_flag == -1) {
266 tlock_flag = 1; // reset the tlock_flag
267 _other_modified = DTM_TRUE;
272 // else tlock_flag == 0, means no lock on this mbox
273 // or tlock_flag == 1, means time out
274 tlock_flag = 1; // reset the tlock_flag
275 _mt_lock = DTM_FALSE;
277 // now let's try the dtmail protocol
278 if (ttdt_Get_Modified(NULL, _path, TT_FILE, NULL, FileShareTimeout)) {
280 _other_modified = DTM_TRUE;
283 _other_modified = DTM_FALSE;
291 FileShare::lockFile(DtMailEnv & error)
297 // If we have the access, then we locked it before. Simply return.
299 if (_have_write_access == DTM_TRUE) {
303 // First step in locking is determining if anyone else has the lock.
304 // If they do, then we need to ask them to save their changes and
307 if (isModified(error) == DTM_TRUE) {
308 DtMailBoolean take_lock = DTM_FALSE; // default is to not request access
310 // calls syncViewAndStoreCallback which then calls syncViewAndStore
314 DtMailEnv::getMessageText(
316 "Another session has this mailbox locked. Request access?");
317 take_lock = _cb_func(DTMC_QUERYLOCK, _path, msg, _cb_data);
320 if (take_lock == DTM_FALSE) {
321 error.setError(DTME_OtherOwnsWrite);
325 // isModified sets _mt_lock to DTM_TRUE is the mailbox is locked
327 if (_mt_lock == DTM_TRUE) {
328 // mailtool style locking
331 mt_msg = msg_create("rulock", _path, TT_NOTICE, NULL);
332 if (mt_msg == NULL) {
333 error.setError(DTME_TTFailure);
337 if (tt_message_send(mt_msg) != TT_OK) {
338 error.setError(DTME_TTFailure);
341 tt_message_destroy(mt_msg);
343 // ttdt style locking
344 ttdt_Save(NULL, _path, TT_FILE, DtMailDamageContext, FileShareTimeout);
347 // Give the other mailer FileShareTimeout seconds to give up the lock
353 if (isModified(error) == DTM_FALSE) {
356 if (time((time_t)NULL) - t_start > FileShareTimeout) {
358 error.setError(DTME_OtherOwnsWrite);
365 // Set this so we don't call our client during this handshake.
367 _pending = PENDING_LOCK;
368 _outstanding = DTM_TRUE;
370 // Now we are ready to lock the mailbox
372 // register this pattern so we can handle messages from mailtool
373 _mt_pattern = tt_pattern_create();
374 tt_pattern_category_set(_mt_pattern, TT_HANDLE);
375 tt_pattern_scope_add(_mt_pattern, TT_FILE);
376 tt_pattern_file_add(_mt_pattern, _path);
377 tt_pattern_op_add(_mt_pattern, "tlock");
378 tt_pattern_op_add(_mt_pattern, "rulock");
379 tt_pattern_callback_add(_mt_pattern, mt_lock_cb);
380 tt_pattern_user_set(_mt_pattern, 1, (void *)this);
382 if (tt_pattern_register(_mt_pattern) != TT_OK) {
383 error.setError(DTME_TTFailure);
387 // Send the message saying we want to be the owner.
388 ttdt_file_event(NULL, TTDT_MODIFIED, _file_pats, 1);
390 // We need to process any messages that have arrived. We will get our own
391 // modified message, which is not terribly interesting. What is interesting
392 // is a modified message from someone else. That means that we have a race
393 // condition where two processes both asked if the file was being modified,
394 // and it wasn't. Then both said they were the owner, which is obviously
395 // wrong so we need to blow both off and make them try again. Hopefully
396 // there is enough randomness in our clients that the race condition will
397 // clear itself up and we won't get here very often.
400 while(_outstanding == DTM_TRUE) {
401 tttk_block_while((XtAppContext)0, &always, 0);
404 if (_other_modified == DTM_TRUE) {
405 // Well, we have a race. Fail this lock as will the other process,
407 error.setError(DTME_OtherOwnsWrite);
411 // Okay, we now have the lock.
412 _have_write_access = DTM_TRUE;
416 FileShare::readOnly(DtMailEnv & error)
418 DtMailBoolean answer = DTM_TRUE; // default is to accept read-only access
423 DtMailEnv::getMessageText(
425 "Unable to obtain lock, open this mailbox as read only?");
426 answer = _cb_func(DTMC_READONLY, _path, msg, _cb_data);
436 FileShare::readWriteOverride(DtMailEnv & error)
438 DtMailBoolean answer = DTM_FALSE; // default is to open for read-only access
443 DtMailEnv::getMessageText(
445 "Unable to obtain lock because system not responding, open this mailbox as read only, read write, or cancel?");
446 answer = _cb_func(DTMC_READWRITEOVERRIDE, _path, msg, _cb_data);
449 if (answer == ((DtMailBoolean)((DTM_FALSE+DTM_TRUE)*2))) {
450 error.setError(DTME_UserInterrupted);
462 FileShare::locked(void)
464 return(_have_write_access);
466 #endif /* DEAD_WOOD */
469 FileShare::fileCB(Tt_message msg,
476 TTHandle *tt_handle = (TTHandle *)clientData;
477 DtMailBoolean answer;
479 if (tt_handle->session->validObjectKey(tt_handle->key) == DTM_FALSE) {
480 // This object has been destroyed. We got here most likely because
481 // ToolTalk is responding to one of our clean up messages. In any
482 // case, fail the message and return.
484 tttk_message_fail(msg, TT_DESKTOP_ECANCELED, "Object destroyed", 1);
488 FileShare * self = tt_handle->self;
492 if (self->_outstanding == DTM_FALSE && !same_proc) {
496 DtMailEnv::getMessageText(
498 "Another user has taken your lock.");
499 self->_cb_func(DTMC_LOSTLOCK, path, msg, self->_cb_data);
501 self->_other_modified = DTM_TRUE;
502 self->_have_write_access = DTM_FALSE;
506 if (self->_outstanding == DTM_TRUE && self->_pending == PENDING_LOCK) {
507 // This could be one of 2 conditions. If the message is
508 // from us, then we have the lock, and we are done.
509 // If not, then someone else is asking for the lock. We
510 // reflect this by giving them the lock.
513 self->_other_modified = DTM_FALSE;
514 self->_have_write_access = DTM_TRUE;
515 self->_outstanding = DTM_FALSE;
518 self->_other_modified = DTM_TRUE;
519 self->_have_write_access = DTM_FALSE;
520 // We haven't seen our own request yet. Leave outstanding
521 // so we can process it before leaving.
526 case TTDT_GET_MODIFIED:
527 tt_message_arg_ival_set(msg, 1, 1);
528 tt_message_reply(msg);
533 // The other process has saved their changes (or tossed them).
534 // At this point we should be able to start modifying the file.
536 self->_other_modified = DTM_FALSE;
537 if (self->_outstanding == DTM_TRUE && self->_pending == PENDING_SAVE) {
538 self->_outstanding = DTM_FALSE;
544 // Someone is asking us to save our changes and close the file.
546 answer = DTM_FALSE; // default is to not give up the lock
551 DtMailEnv::getMessageText(
553 "Another user would like your lock.");
554 answer = self->_cb_func(DTMC_UNLOCK, path, msg, self->_cb_data);
557 if (answer == DTM_TRUE) {
558 tt_message_reply(msg);
560 tttk_message_fail(msg, TT_DESKTOP_EACCES, 0, 0);
565 // Other messages, we simply smile and say thank you.:-)
567 tt_message_reply(msg);
572 tt_message_destroy(msg);