mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Allow the user to open a file if the lock wasn't obtained
after going through a dialog. git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@2962 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
parent
7842e38587
commit
ce05f4893d
227
src/FileDialog.c
227
src/FileDialog.c
@ -37,78 +37,86 @@
|
|||||||
#include "file-history.h"
|
#include "file-history.h"
|
||||||
|
|
||||||
/* This static indicates the debugging module that this .o belongs to. */
|
/* This static indicates the debugging module that this .o belongs to. */
|
||||||
// static short module = MOD_GUI;
|
/* static short module = MOD_GUI; */
|
||||||
|
|
||||||
/** GLOBALS *********************************************************/
|
/** GLOBALS *********************************************************/
|
||||||
static Session *current_session = NULL;
|
static Session *current_session = NULL;
|
||||||
static AccountGroup *topgroup = NULL; /* the current top of the hierarchy */
|
static AccountGroup *topgroup = NULL; /* the current top of the hierarchy */
|
||||||
|
|
||||||
/********************************************************************\
|
|
||||||
* fileMenubarCB -- handles file menubar choices *
|
|
||||||
\********************************************************************/
|
|
||||||
|
|
||||||
#define SHOW_IO_ERR_MSG(io_error) { \
|
/* ======================================================== */
|
||||||
switch (io_error) { \
|
|
||||||
case ERR_FILEIO_NO_ERROR: \
|
static gboolean
|
||||||
break; \
|
show_file_error (int io_error, char *newfile)
|
||||||
case ERR_FILEIO_FILE_NOT_FOUND: \
|
{
|
||||||
sprintf (buf, FILE_NOT_FOUND_MSG, newfile); \
|
gboolean uh_oh = FALSE;
|
||||||
gnc_error_dialog (buf); \
|
char *buf = NULL;
|
||||||
uh_oh = 1; \
|
|
||||||
break; \
|
switch (io_error)
|
||||||
case ERR_FILEIO_FILE_EMPTY: \
|
{
|
||||||
sprintf (buf, FILE_EMPTY_MSG, newfile); \
|
case ERR_FILEIO_NO_ERROR:
|
||||||
gnc_error_dialog (buf); \
|
break;
|
||||||
uh_oh = 1; \
|
case ERR_FILEIO_FILE_NOT_FOUND:
|
||||||
break; \
|
buf = g_strdup_printf (FILE_NOT_FOUND_MSG, newfile);
|
||||||
case ERR_FILEIO_FILE_TOO_NEW: \
|
gnc_error_dialog (buf);
|
||||||
gnc_error_dialog ( FILE_TOO_NEW_MSG); \
|
uh_oh = TRUE;
|
||||||
uh_oh = 1; \
|
break;
|
||||||
break; \
|
case ERR_FILEIO_FILE_EMPTY:
|
||||||
case ERR_FILEIO_FILE_TOO_OLD: \
|
buf = g_strdup_printf (FILE_EMPTY_MSG, newfile);
|
||||||
if (!gnc_verify_dialog( FILE_TOO_OLD_MSG, TRUE )) { \
|
gnc_error_dialog (buf);
|
||||||
xaccFreeAccountGroup (newgrp); \
|
uh_oh = TRUE;
|
||||||
newgrp = NULL; \
|
break;
|
||||||
uh_oh = 1; \
|
case ERR_FILEIO_FILE_TOO_NEW:
|
||||||
} \
|
gnc_error_dialog (FILE_TOO_NEW_MSG);
|
||||||
break; \
|
uh_oh = TRUE;
|
||||||
case ERR_FILEIO_FILE_BAD_READ: \
|
break;
|
||||||
if (!gnc_verify_dialog( FILE_BAD_READ_MSG, TRUE )) { \
|
case ERR_FILEIO_FILE_TOO_OLD:
|
||||||
xaccFreeAccountGroup (newgrp); \
|
if (!gnc_verify_dialog (FILE_TOO_OLD_MSG, TRUE))
|
||||||
newgrp = NULL; \
|
uh_oh = TRUE;
|
||||||
uh_oh = 1; \
|
break;
|
||||||
} \
|
case ERR_FILEIO_FILE_BAD_READ:
|
||||||
break; \
|
if (!gnc_verify_dialog (FILE_BAD_READ_MSG, TRUE))
|
||||||
default: \
|
uh_oh = TRUE;
|
||||||
break; \
|
break;
|
||||||
} \
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free (buf);
|
||||||
|
|
||||||
|
return uh_oh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#define SHOW_LOCK_ERR_MSG(session) \
|
/* ======================================================== */
|
||||||
{ \
|
|
||||||
int norr = xaccSessionGetError (session); \
|
static gboolean
|
||||||
if (ETXTBSY == norr) \
|
show_session_error(Session *session, char *newfile)
|
||||||
{ \
|
{
|
||||||
sprintf (buf, FMB_LOCKED_MSG, newfile); \
|
int norr = xaccSessionGetError (session);
|
||||||
gnc_error_dialog (buf); \
|
gboolean uh_oh = FALSE;
|
||||||
uh_oh = 1; \
|
char *buf = NULL;
|
||||||
} \
|
|
||||||
else \
|
if (ETXTBSY == norr)
|
||||||
if (ERANGE == norr) \
|
{
|
||||||
{ \
|
uh_oh = TRUE;
|
||||||
sprintf (buf, FILE_NOT_FOUND_MSG, newfile); \
|
}
|
||||||
gnc_error_dialog (buf); \
|
else if (ERANGE == norr)
|
||||||
uh_oh = 1; \
|
{
|
||||||
} \
|
buf = g_strdup_printf (FILE_NOT_FOUND_MSG, newfile);
|
||||||
else \
|
gnc_error_dialog (buf);
|
||||||
if (norr) \
|
uh_oh = TRUE;
|
||||||
{ \
|
}
|
||||||
sprintf (buf, FMB_INVALID_MSG, newfile); \
|
else if (norr)
|
||||||
gnc_error_dialog (buf); \
|
{
|
||||||
uh_oh = 1; \
|
buf = (FMB_INVALID_MSG, newfile);
|
||||||
} \
|
gnc_error_dialog (buf);
|
||||||
} \
|
uh_oh = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(buf);
|
||||||
|
|
||||||
|
return uh_oh;
|
||||||
|
}
|
||||||
|
|
||||||
/* ======================================================== */
|
/* ======================================================== */
|
||||||
|
|
||||||
@ -191,6 +199,29 @@ gncFileQuerySave (void)
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ======================================================== */
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gncLockFailHandler (const char *file)
|
||||||
|
{
|
||||||
|
const char *format = _("Gnucash could not obtain the lock for\n"
|
||||||
|
" %s.\n"
|
||||||
|
"That file may be in use by another user,\n"
|
||||||
|
"in which case you should not open the file.\n"
|
||||||
|
"\nDo you want to proceed with opening the file?");
|
||||||
|
char *message;
|
||||||
|
gboolean result;
|
||||||
|
|
||||||
|
if (file == NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
message = g_strdup_printf (format, file);
|
||||||
|
result = gnc_verify_dialog (message, FALSE);
|
||||||
|
g_free (message);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/* ======================================================== */
|
/* ======================================================== */
|
||||||
/* private utilities for file open; done in two stages */
|
/* private utilities for file open; done in two stages */
|
||||||
|
|
||||||
@ -199,16 +230,17 @@ gncPostFileOpen (const char * filename)
|
|||||||
{
|
{
|
||||||
Session *newsess;
|
Session *newsess;
|
||||||
AccountGroup *oldgrp;
|
AccountGroup *oldgrp;
|
||||||
int io_error, uh_oh=0;
|
gboolean uh_oh = FALSE;
|
||||||
char buf[BUFSIZE];
|
int io_error;
|
||||||
AccountGroup *newgrp;
|
AccountGroup *newgrp;
|
||||||
char * newfile;
|
char * newfile;
|
||||||
|
|
||||||
if (!filename) return;
|
if (!filename) return;
|
||||||
newfile = xaccResolveFilePath (filename);
|
newfile = xaccResolveFilePath (filename);
|
||||||
if (!newfile) {
|
if (!newfile) {
|
||||||
sprintf (buf, FILE_NOT_FOUND_MSG, filename);
|
char *buf = g_strdup_printf (FILE_NOT_FOUND_MSG, filename);
|
||||||
gnc_error_dialog (buf);
|
gnc_error_dialog (buf);
|
||||||
|
g_free(buf);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,26 +259,32 @@ gncPostFileOpen (const char * filename)
|
|||||||
* switchover is not something we want to keep in a journal. */
|
* switchover is not something we want to keep in a journal. */
|
||||||
gnc_set_busy_cursor(NULL);
|
gnc_set_busy_cursor(NULL);
|
||||||
xaccLogDisable();
|
xaccLogDisable();
|
||||||
newgrp = xaccSessionBeginFile (newsess, newfile);
|
newgrp = xaccSessionBeginFile (newsess, newfile, gncLockFailHandler);
|
||||||
xaccLogEnable();
|
xaccLogEnable();
|
||||||
gnc_unset_busy_cursor(NULL);
|
gnc_unset_busy_cursor(NULL);
|
||||||
|
|
||||||
/* check for session errors, put up appropriate dialog */
|
/* check for session errors, put up appropriate dialog */
|
||||||
SHOW_LOCK_ERR_MSG (newsess);
|
uh_oh = show_session_error (newsess, newfile);
|
||||||
|
|
||||||
if (!uh_oh)
|
if (!uh_oh)
|
||||||
{
|
{
|
||||||
/* check for i/o error, put up appropriate error message */
|
/* check for i/o error, put up appropriate error message */
|
||||||
io_error = xaccGetFileIOError();
|
io_error = xaccGetFileIOError();
|
||||||
SHOW_IO_ERR_MSG(io_error);
|
uh_oh = show_file_error (io_error, newfile);
|
||||||
|
if (uh_oh)
|
||||||
|
{
|
||||||
|
xaccFreeAccountGroup (newgrp);
|
||||||
|
newgrp = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* Umm, came up empty-handed, i.e. the file was not found. */
|
/* Umm, came up empty-handed, i.e. the file was not found. */
|
||||||
/* This is almost certainly not what the user wanted. */
|
/* This is almost certainly not what the user wanted. */
|
||||||
if (!uh_oh && !newgrp && !io_error)
|
if (!uh_oh && !newgrp && !io_error)
|
||||||
{
|
{
|
||||||
sprintf (buf, FILE_NOT_FOUND_MSG, newfile);
|
char *buf = g_strdup_printf (FILE_NOT_FOUND_MSG, newfile);
|
||||||
gnc_error_dialog ( buf);
|
gnc_error_dialog (buf);
|
||||||
uh_oh = 1;
|
g_free (buf);
|
||||||
|
uh_oh = TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,9 +296,9 @@ gncPostFileOpen (const char * filename)
|
|||||||
|
|
||||||
/* well, no matter what, I think its a good idea to have
|
/* well, no matter what, I think its a good idea to have
|
||||||
* a topgroup around. For example, early in the gnucash startup
|
* a topgroup around. For example, early in the gnucash startup
|
||||||
* sequence, the user opens a file ... if this open fails for any
|
* sequence, the user opens a file; if this open fails for any
|
||||||
* reason, we don't want to leave them high & dry without a topgroup,
|
* reason, we don't want to leave them high & dry without a topgroup,
|
||||||
* because if user continues, then bad things will happen ...
|
* because if the user continues, then bad things will happen.
|
||||||
*/
|
*/
|
||||||
if (NULL == topgroup)
|
if (NULL == topgroup)
|
||||||
{
|
{
|
||||||
@ -343,7 +381,6 @@ gncFileSave (void)
|
|||||||
{
|
{
|
||||||
AccountGroup *newgrp = NULL;
|
AccountGroup *newgrp = NULL;
|
||||||
char * newfile;
|
char * newfile;
|
||||||
char buf[BUFSIZE];
|
|
||||||
int io_error, norr, uh_oh = 0;
|
int io_error, norr, uh_oh = 0;
|
||||||
|
|
||||||
/* hack alert -- Somehow make sure all in-progress edits get committed! */
|
/* hack alert -- Somehow make sure all in-progress edits get committed! */
|
||||||
@ -392,7 +429,13 @@ gncFileSave (void)
|
|||||||
io_error = xaccGetFileIOError();
|
io_error = xaccGetFileIOError();
|
||||||
newfile = xaccSessionGetFilePath(current_session);
|
newfile = xaccSessionGetFilePath(current_session);
|
||||||
gnc_history_add_file(newfile);
|
gnc_history_add_file(newfile);
|
||||||
SHOW_IO_ERR_MSG(io_error);
|
|
||||||
|
uh_oh = show_file_error (io_error, newfile);
|
||||||
|
if (uh_oh)
|
||||||
|
{
|
||||||
|
xaccFreeAccountGroup (newgrp);
|
||||||
|
newgrp = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* going down -- abandon ship */
|
/* going down -- abandon ship */
|
||||||
if (uh_oh) return;
|
if (uh_oh) return;
|
||||||
@ -411,8 +454,8 @@ gncFileSaveAs (void)
|
|||||||
char *newfile;
|
char *newfile;
|
||||||
AccountGroup *newgrp;
|
AccountGroup *newgrp;
|
||||||
char * oldfile;
|
char * oldfile;
|
||||||
char buf[BUFSIZE];
|
int io_error;
|
||||||
int io_error, uh_oh = 0;
|
gboolean uh_oh = FALSE;
|
||||||
|
|
||||||
filename = fileBox(SAVE_STR, "*.gnc");
|
filename = fileBox(SAVE_STR, "*.gnc");
|
||||||
if (!filename) return;
|
if (!filename) return;
|
||||||
@ -424,8 +467,9 @@ gncFileSaveAs (void)
|
|||||||
*/
|
*/
|
||||||
newfile = xaccResolveFilePath (filename);
|
newfile = xaccResolveFilePath (filename);
|
||||||
if (!newfile) {
|
if (!newfile) {
|
||||||
sprintf (buf, FILE_NOT_FOUND_MSG, filename);
|
char *buf = g_strdup_printf (FILE_NOT_FOUND_MSG, filename);
|
||||||
gnc_error_dialog (buf);
|
gnc_error_dialog (buf);
|
||||||
|
g_free (buf);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
oldfile = xaccSessionGetFilePath (current_session);
|
oldfile = xaccSessionGetFilePath (current_session);
|
||||||
@ -449,17 +493,22 @@ gncFileSaveAs (void)
|
|||||||
* edit; the mass deletetion of accounts and transactions during
|
* edit; the mass deletetion of accounts and transactions during
|
||||||
* switchover is not something we want to keep in a journal. */
|
* switchover is not something we want to keep in a journal. */
|
||||||
xaccLogDisable();
|
xaccLogDisable();
|
||||||
newgrp = xaccSessionBeginFile (newsess, newfile);
|
newgrp = xaccSessionBeginFile (newsess, newfile, gncLockFailHandler);
|
||||||
xaccLogEnable();
|
xaccLogEnable();
|
||||||
|
|
||||||
/* check for session errors (e.g. file locked by another user) */
|
/* check for session errors (e.g. file locked by another user) */
|
||||||
SHOW_LOCK_ERR_MSG (newsess);
|
uh_oh = show_session_error (newsess, newfile);
|
||||||
|
|
||||||
if (!uh_oh)
|
if (!uh_oh)
|
||||||
{
|
{
|
||||||
/* check for i/o error, put up appropriate error message */
|
/* check for i/o error, put up appropriate error message */
|
||||||
io_error = xaccGetFileIOError();
|
io_error = xaccGetFileIOError();
|
||||||
SHOW_IO_ERR_MSG(io_error);
|
uh_oh = show_file_error (io_error, newfile);
|
||||||
|
if (uh_oh)
|
||||||
|
{
|
||||||
|
xaccFreeAccountGroup (newgrp);
|
||||||
|
newgrp = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* going down -- abandon ship */
|
/* going down -- abandon ship */
|
||||||
@ -496,10 +545,15 @@ gncFileSaveAs (void)
|
|||||||
if (newgrp)
|
if (newgrp)
|
||||||
{
|
{
|
||||||
char *tmpmsg;
|
char *tmpmsg;
|
||||||
tmpmsg = alloca (strlen (FMB_EEXIST_MSG) + strlen (newfile));
|
gboolean result;
|
||||||
sprintf (tmpmsg, FMB_EEXIST_MSG, newfile);
|
|
||||||
|
tmpmsg = g_strdup_printf (FMB_EEXIST_MSG, newfile);
|
||||||
|
result = gnc_verify_dialog (tmpmsg, FALSE);
|
||||||
|
g_free (tmpmsg);
|
||||||
|
|
||||||
/* if user says cancel, we should break out */
|
/* if user says cancel, we should break out */
|
||||||
if (! gnc_verify_dialog (tmpmsg, FALSE)) return;
|
if (!result)
|
||||||
|
return;
|
||||||
|
|
||||||
/* Whoa-ok. Blow away the previous file.
|
/* Whoa-ok. Blow away the previous file.
|
||||||
* Do not disable logging ... we want to capture the
|
* Do not disable logging ... we want to capture the
|
||||||
@ -537,7 +591,6 @@ gncFileQuit (void)
|
|||||||
xaccSessionEnd (current_session);
|
xaccSessionEnd (current_session);
|
||||||
xaccSessionDestroy (current_session);
|
xaccSessionDestroy (current_session);
|
||||||
current_session = NULL;
|
current_session = NULL;
|
||||||
// xaccGroupWindowDestroy (grp);
|
|
||||||
xaccFreeAccountGroup (grp);
|
xaccFreeAccountGroup (grp);
|
||||||
topgroup = NULL;
|
topgroup = NULL;
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,3 @@
|
|||||||
/*
|
|
||||||
* FILE:
|
|
||||||
* Session.c
|
|
||||||
*
|
|
||||||
* FUNCTION:
|
|
||||||
* Provide wrappers for initiating/concluding a file-editing session.
|
|
||||||
*
|
|
||||||
* HISTORY:
|
|
||||||
* Created by Linas Vepstas December 1998
|
|
||||||
* Copyright (c) 1998-2000 Linas Vepstas
|
|
||||||
*/
|
|
||||||
|
|
||||||
/********************************************************************\
|
/********************************************************************\
|
||||||
* This program is free software; you can redistribute it and/or *
|
* This program is free software; you can redistribute it and/or *
|
||||||
* modify it under the terms of the GNU General Public License as *
|
* modify it under the terms of the GNU General Public License as *
|
||||||
@ -29,6 +17,18 @@
|
|||||||
* Boston, MA 02111-1307, USA gnu@gnu.org *
|
* Boston, MA 02111-1307, USA gnu@gnu.org *
|
||||||
\********************************************************************/
|
\********************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FILE:
|
||||||
|
* Session.c
|
||||||
|
*
|
||||||
|
* FUNCTION:
|
||||||
|
* Provide wrappers for initiating/concluding a file-editing session.
|
||||||
|
*
|
||||||
|
* HISTORY:
|
||||||
|
* Created by Linas Vepstas December 1998
|
||||||
|
* Copyright (c) 1998-2000 Linas Vepstas
|
||||||
|
*/
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
@ -81,7 +81,7 @@ Session *
|
|||||||
xaccMallocSession (void)
|
xaccMallocSession (void)
|
||||||
{
|
{
|
||||||
Session *sess;
|
Session *sess;
|
||||||
sess = (Session *) malloc (sizeof (Session));
|
sess = g_new(Session, 1);
|
||||||
|
|
||||||
xaccInitSession (sess);
|
xaccInitSession (sess);
|
||||||
return sess;
|
return sess;
|
||||||
@ -179,7 +179,7 @@ xaccSessionBegin (Session *sess, const char * sid)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* add 5 to space past 'file:' */
|
/* add 5 to space past 'file:' */
|
||||||
retval = xaccSessionBeginFile (sess, sid+5);
|
retval = xaccSessionBeginFile (sess, sid+5, NULL);
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
@ -217,12 +217,79 @@ extern Backend * pgendNew (void);
|
|||||||
|
|
||||||
/* ============================================================== */
|
/* ============================================================== */
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
xaccSessionGetFileLock (Session *sess)
|
||||||
|
{
|
||||||
|
struct stat statbuf;
|
||||||
|
char pathbuf[PATH_MAX];
|
||||||
|
char *path = NULL;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = stat (sess->lockfile, &statbuf);
|
||||||
|
if (!rc) {
|
||||||
|
/* oops .. file is all locked up .. */
|
||||||
|
sess->errtype = ETXTBSY;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
sess->lockfd = open (sess->lockfile, O_RDWR | O_CREAT | O_EXCL , 0);
|
||||||
|
if (0 > sess->lockfd) {
|
||||||
|
/* oops .. file is all locked up .. */
|
||||||
|
sess->errtype = ETXTBSY;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* OK, now work around some NFS atomic lock race condition
|
||||||
|
* mumbo-jumbo. We do this by linking a unique file, and
|
||||||
|
* then examing the link count. At least that's what the
|
||||||
|
* NFS programmers guide suggests.
|
||||||
|
* Note: the "unique filename" must be unique for the
|
||||||
|
* triplet filename-host-process, otherwise accidental
|
||||||
|
* aliases can occur.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* apparently, even this code may not work for some NFS
|
||||||
|
* implementations. In the long run, I am told that
|
||||||
|
* ftp.debian.org
|
||||||
|
* /pub/debian/dists/unstable/main/source/libs/liblockfile_0.1-6.tar.gz
|
||||||
|
* provides a better long-term solution.
|
||||||
|
*/
|
||||||
|
|
||||||
|
strcpy (pathbuf, sess->lockfile);
|
||||||
|
path = strrchr (pathbuf, '.');
|
||||||
|
sprintf (path, ".%lx.%d.LNK", gethostid(), getpid());
|
||||||
|
link (sess->lockfile, pathbuf);
|
||||||
|
rc = stat (sess->lockfile, &statbuf);
|
||||||
|
if (rc) {
|
||||||
|
/* oops .. stat failed! This can't happen! */
|
||||||
|
sess->errtype = ETXTBSY;
|
||||||
|
unlink (pathbuf);
|
||||||
|
close (sess->lockfd);
|
||||||
|
unlink (sess->lockfile);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (2 != statbuf.st_nlink) {
|
||||||
|
/* oops .. stat failed! This can't happen! */
|
||||||
|
sess->errtype = ETXTBSY;
|
||||||
|
unlink (pathbuf);
|
||||||
|
close (sess->lockfd);
|
||||||
|
unlink (sess->lockfile);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
sess->linkfile = g_strdup (pathbuf);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================== */
|
||||||
|
|
||||||
AccountGroup *
|
AccountGroup *
|
||||||
xaccSessionBeginFile (Session *sess, const char * filefrag)
|
xaccSessionBeginFile (Session *sess, const char * filefrag,
|
||||||
|
SessionLockFailHandler handler)
|
||||||
{
|
{
|
||||||
struct stat statbuf;
|
struct stat statbuf;
|
||||||
char pathbuf[PATH_MAX];
|
|
||||||
char *path = NULL;
|
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if (!sess) return NULL;
|
if (!sess) return NULL;
|
||||||
@ -252,87 +319,27 @@ xaccSessionBeginFile (Session *sess, const char * filefrag)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Store the sessionid URL also ... */
|
/* Store the sessionid URL also ... */
|
||||||
strcpy (pathbuf, "file:");
|
sess->sessionid = g_strconcat ("file:", filefrag, NULL);
|
||||||
strcat (pathbuf, filefrag);
|
|
||||||
sess->sessionid = strdup (pathbuf);
|
|
||||||
|
|
||||||
/* ---------------------------------------------------- */
|
/* ---------------------------------------------------- */
|
||||||
/* We should now have a fully resolved path name.
|
/* We should now have a fully resolved path name.
|
||||||
* Lets see if we can get a lock on it.
|
* Lets see if we can get a lock on it. */
|
||||||
*/
|
|
||||||
|
|
||||||
sess->lockfile = malloc (strlen (sess->fullpath) + 5);
|
sess->lockfile = g_strconcat(sess->fullpath, ".LCK", NULL);
|
||||||
strcpy (sess->lockfile, sess->fullpath);
|
|
||||||
strcat (sess->lockfile, ".LCK");
|
|
||||||
|
|
||||||
rc = stat (sess->lockfile, &statbuf);
|
|
||||||
if (!rc) {
|
|
||||||
/* oops .. file is all locked up .. */
|
|
||||||
sess->errtype = ETXTBSY;
|
|
||||||
free (sess->sessionid); sess->sessionid = NULL;
|
|
||||||
free (sess->fullpath); sess->fullpath = NULL;
|
|
||||||
free (sess->lockfile); sess->lockfile = NULL;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
sess->lockfd = open (sess->lockfile, O_RDWR | O_CREAT | O_EXCL , 0);
|
|
||||||
if (0 > sess->lockfd) {
|
|
||||||
/* oops .. file is all locked up .. */
|
|
||||||
sess->errtype = ETXTBSY;
|
|
||||||
free (sess->sessionid); sess->sessionid = NULL;
|
|
||||||
free (sess->fullpath); sess->fullpath = NULL;
|
|
||||||
free (sess->lockfile); sess->lockfile = NULL;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* OK, now work around some NFS atomic lock race condition
|
if (!xaccSessionGetFileLock (sess)) {
|
||||||
* mumbo-jumbo. We do this by linking a unique file, and
|
if (!handler || !handler (sess->fullpath)) {
|
||||||
* then examing the link count. At least that's what the
|
g_free (sess->sessionid); sess->sessionid = NULL;
|
||||||
* NFS programmers guide suggests.
|
g_free (sess->fullpath); sess->fullpath = NULL;
|
||||||
* Note: the "unique filename" must be unique for the
|
g_free (sess->lockfile); sess->lockfile = NULL;
|
||||||
* triplet filename-host-process, otherwise accidental
|
return NULL;
|
||||||
* aliases can occur.
|
}
|
||||||
*/
|
|
||||||
/* appearently, even this code may not work for some NFS
|
|
||||||
* implementations. In the long run, I am told that
|
|
||||||
* ftp.debian.org
|
|
||||||
* /pub/debian/dists/unstable/main/source/libs/liblockfile_0.1-6.tar.gz
|
|
||||||
* provides a better long-term solution.
|
|
||||||
*/
|
|
||||||
strcpy (pathbuf, sess->lockfile);
|
|
||||||
path = strrchr (pathbuf, '.');
|
|
||||||
sprintf (path, ".%lx.%d.LNK", gethostid(), getpid());
|
|
||||||
link (sess->lockfile, pathbuf);
|
|
||||||
rc = stat (sess->lockfile, &statbuf);
|
|
||||||
if (rc) {
|
|
||||||
/* oops .. stat failed! This can't happen! */
|
|
||||||
sess->errtype = ETXTBSY;
|
|
||||||
unlink (pathbuf);
|
|
||||||
close (sess->lockfd);
|
|
||||||
unlink (sess->lockfile);
|
|
||||||
free (sess->sessionid); sess->sessionid = NULL;
|
|
||||||
free (sess->fullpath); sess->fullpath = NULL;
|
|
||||||
free (sess->lockfile); sess->lockfile = NULL;
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (2 != statbuf.st_nlink) {
|
|
||||||
/* oops .. stat failed! This can't happen! */
|
|
||||||
sess->errtype = ETXTBSY;
|
|
||||||
unlink (pathbuf);
|
|
||||||
close (sess->lockfd);
|
|
||||||
unlink (sess->lockfile);
|
|
||||||
free (sess->sessionid); sess->sessionid = NULL;
|
|
||||||
free (sess->fullpath); sess->fullpath = NULL;
|
|
||||||
free (sess->lockfile); sess->lockfile = NULL;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
sess->linkfile = strdup (pathbuf);
|
|
||||||
|
|
||||||
/* ---------------------------------------------------- */
|
/* ---------------------------------------------------- */
|
||||||
/* OK, if we've gotten this far, then we've succesfully obtained
|
/* OK, if we've gotten this far, then we've succesfully obtained
|
||||||
* an atomic lock on the file. Go read the file contents ...
|
* an atomic lock on the file. Go read the file contents if it
|
||||||
* well, read it only if it exists ...
|
* exists. */
|
||||||
*/
|
|
||||||
|
|
||||||
sess->errtype = 0;
|
sess->errtype = 0;
|
||||||
sess->topgroup = NULL;
|
sess->topgroup = NULL;
|
||||||
@ -387,13 +394,20 @@ xaccSessionEnd (Session *sess)
|
|||||||
if (sess->linkfile) unlink (sess->linkfile);
|
if (sess->linkfile) unlink (sess->linkfile);
|
||||||
if (0 < sess->lockfd) close (sess->lockfd);
|
if (0 < sess->lockfd) close (sess->lockfd);
|
||||||
if (sess->lockfile) unlink (sess->lockfile);
|
if (sess->lockfile) unlink (sess->lockfile);
|
||||||
if (sess->sessionid) free (sess->sessionid); sess->sessionid = NULL;
|
|
||||||
if (sess->fullpath) free (sess->fullpath); sess->fullpath = NULL;
|
|
||||||
if (sess->lockfile) free (sess->lockfile); sess->lockfile = NULL;
|
|
||||||
if (sess->linkfile) free (sess->linkfile); sess->linkfile = NULL;
|
|
||||||
sess->topgroup = NULL;
|
|
||||||
|
|
||||||
return;
|
g_free (sess->sessionid);
|
||||||
|
sess->sessionid = NULL;
|
||||||
|
|
||||||
|
g_free (sess->fullpath);
|
||||||
|
sess->fullpath = NULL;
|
||||||
|
|
||||||
|
g_free (sess->lockfile);
|
||||||
|
sess->lockfile = NULL;
|
||||||
|
|
||||||
|
g_free (sess->linkfile);
|
||||||
|
sess->linkfile = NULL;
|
||||||
|
|
||||||
|
sess->topgroup = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -401,7 +415,7 @@ xaccSessionDestroy (Session *sess)
|
|||||||
{
|
{
|
||||||
if (!sess) return;
|
if (!sess) return;
|
||||||
xaccSessionEnd (sess);
|
xaccSessionEnd (sess);
|
||||||
free (sess);
|
g_free (sess);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -415,29 +429,32 @@ MakeHomeDir (void)
|
|||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
struct stat statbuf;
|
struct stat statbuf;
|
||||||
char *home, *path;
|
char *home;
|
||||||
|
char *path;
|
||||||
|
char *data;
|
||||||
|
|
||||||
/* Punt. Can't figure out where home is. */
|
/* Punt. Can't figure out where home is. */
|
||||||
home = getenv ("HOME");
|
home = getenv ("HOME");
|
||||||
if (!home) return;
|
if (!home) return;
|
||||||
|
|
||||||
path = alloca (strlen (home) +50);
|
path = g_strconcat(home, "/.gnucash", NULL);
|
||||||
strcpy (path, home);
|
|
||||||
strcat (path, "/.gnucash");
|
|
||||||
|
|
||||||
rc = stat (path, &statbuf);
|
rc = stat (path, &statbuf);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
/* assume that the stat failed only because the dir is absent,
|
/* assume that the stat failed only because the dir is absent,
|
||||||
* and not because its read-protected or other error.
|
* and not because its read-protected or other error.
|
||||||
* Go ahead and make it. Don't bother much with checking mkdir
|
* Go ahead and make it. Don't bother much with checking mkdir
|
||||||
* for errors; seems pointless ... */
|
* for errors; seems pointless. */
|
||||||
mkdir (path, S_IRWXU); /* perms = S_IRWXU = 0700 */
|
mkdir (path, S_IRWXU); /* perms = S_IRWXU = 0700 */
|
||||||
}
|
}
|
||||||
|
|
||||||
strcat (path, "/data");
|
data = g_strconcat (path, "/data");
|
||||||
rc = stat (path, &statbuf);
|
rc = stat (data, &statbuf);
|
||||||
if (rc)
|
if (rc)
|
||||||
mkdir (path, S_IRWXU);
|
mkdir (data, S_IRWXU);
|
||||||
|
|
||||||
|
g_free (path);
|
||||||
|
g_free (data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================================== */
|
/* ============================================================== */
|
||||||
|
@ -88,7 +88,12 @@ void xaccSessionDestroy (Session *);
|
|||||||
*
|
*
|
||||||
* The xaccSessionBeginFile() routine is identical to the xaccSessionBegin()
|
* The xaccSessionBeginFile() routine is identical to the xaccSessionBegin()
|
||||||
* routine, except that the argument is a filename (i.e. the five
|
* routine, except that the argument is a filename (i.e. the five
|
||||||
* letters "file:" should not be prepended).
|
* letters "file:" should not be prepended) and there is an additional
|
||||||
|
* function argument. This function is called if xaccSessionBeginFile
|
||||||
|
* fails to obtain a lock for the file. If it returns true, the file
|
||||||
|
* is loaded anyway. If it returns false, or the handler is NULL, a
|
||||||
|
* failed lock attempt will abort the load. The lock fail handler is
|
||||||
|
* passed the filename of the data file being loaded.
|
||||||
*
|
*
|
||||||
* The xaccSessionGetFilePath() routine returns the fully-qualified file
|
* The xaccSessionGetFilePath() routine returns the fully-qualified file
|
||||||
* path for the session. That is, if a relative or partial filename
|
* path for the session. That is, if a relative or partial filename
|
||||||
@ -168,8 +173,11 @@ void xaccSessionDestroy (Session *);
|
|||||||
* xaccSessionDestroy (sess);
|
* xaccSessionDestroy (sess);
|
||||||
* */
|
* */
|
||||||
|
|
||||||
|
typedef gboolean (*SessionLockFailHandler)(const char *file);
|
||||||
|
|
||||||
AccountGroup * xaccSessionBegin (Session *, const char * sessionid);
|
AccountGroup * xaccSessionBegin (Session *, const char * sessionid);
|
||||||
AccountGroup * xaccSessionBeginFile (Session *, const char * filename);
|
AccountGroup * xaccSessionBeginFile (Session *, const char * filename,
|
||||||
|
SessionLockFailHandler handler);
|
||||||
int xaccSessionGetError (Session *);
|
int xaccSessionGetError (Session *);
|
||||||
AccountGroup * xaccSessionGetGroup (Session *);
|
AccountGroup * xaccSessionGetGroup (Session *);
|
||||||
void xaccSessionSetGroup (Session *, AccountGroup *topgroup);
|
void xaccSessionSetGroup (Session *, AccountGroup *topgroup);
|
||||||
|
Loading…
Reference in New Issue
Block a user