diff options
author | Kaleb Keithley <kaleb@freedesktop.org> | 2003-11-14 15:54:53 +0000 |
---|---|---|
committer | Kaleb Keithley <kaleb@freedesktop.org> | 2003-11-14 15:54:53 +0000 |
commit | f4d5033f594bf278ff1171091219b3c28dc45446 (patch) | |
tree | ac5a9d76006c7b9ef385ac41b5d5d2a6a92903a3 |
R6.6 is the Xorg base-lineXORG-MAINXORG-STABLE
-rw-r--r-- | save.c | 465 | ||||
-rw-r--r-- | smproxy.c | 1314 | ||||
-rw-r--r-- | smproxy.h | 101 | ||||
-rw-r--r-- | smproxy.man | 72 |
4 files changed, 1952 insertions, 0 deletions
@@ -0,0 +1,465 @@ +/* $Xorg: save.c,v 1.4 2001/02/09 02:05:35 xorgcvs Exp $ */ +/****************************************************************************** + +Copyright 1994, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + +Author: Ralph Mor, X Consortium +******************************************************************************/ + +#include "smproxy.h" + +typedef struct ProxyFileEntry +{ + struct ProxyFileEntry *next; + int tag; + char *client_id; + XClassHint class; + char *wm_name; + int wm_command_count; + char **wm_command; +} ProxyFileEntry; + +ProxyFileEntry *proxyFileHead = NULL; + +extern WinInfo *win_head; + + + +static int +write_byte (file, b) + +FILE *file; +unsigned char b; + +{ + if (fwrite ((char *) &b, 1, 1, file) != 1) + return 0; + return 1; +} + + +static int +write_short (file, s) + +FILE *file; +unsigned short s; + +{ + unsigned char file_short[2]; + + file_short[0] = (s & (unsigned)0xff00) >> 8; + file_short[1] = s & 0xff; + if (fwrite ((char *) file_short, (int) sizeof (file_short), 1, file) != 1) + return 0; + return 1; +} + + +static int +write_counted_string (file, string) + +FILE *file; +char *string; + +{ + if (string) + { + unsigned char count = strlen (string); + + if (write_byte (file, count) == 0) + return 0; + if (fwrite (string, (int) sizeof (char), (int) count, file) != count) + return 0; + } + else + { + if (write_byte (file, 0) == 0) + return 0; + } + + return 1; +} + + + +static int +read_byte (file, bp) + +FILE *file; +unsigned char *bp; + +{ + if (fread ((char *) bp, 1, 1, file) != 1) + return 0; + return 1; +} + + +static int +read_short (file, shortp) + +FILE *file; +unsigned short *shortp; + +{ + unsigned char file_short[2]; + + if (fread ((char *) file_short, (int) sizeof (file_short), 1, file) != 1) + return 0; + *shortp = file_short[0] * 256 + file_short[1]; + return 1; +} + + +static int +read_counted_string (file, stringp) + +FILE *file; +char **stringp; + +{ + unsigned char len; + char *data; + + if (read_byte (file, &len) == 0) + return 0; + if (len == 0) { + data = 0; + } else { + data = (char *) malloc ((unsigned) len + 1); + if (!data) + return 0; + if (fread (data, (int) sizeof (char), (int) len, file) != len) { + free (data); + return 0; + } + data[len] = '\0'; + } + *stringp = data; + return 1; +} + + + +/* + * An entry in the .smproxy file looks like this: + * + * FIELD BYTES + * ----- ---- + * client ID len 1 + * client ID LIST of bytes + * WM_CLASS "res name" length 1 + * WM_CLASS "res name" LIST of bytes + * WM_CLASS "res class" length 1 + * WM_CLASS "res class" LIST of bytes + * WM_NAME length 1 + * WM_NAME LIST of bytes + * WM_COMMAND arg count 1 + * For each arg in WM_COMMAND + * arg length 1 + * arg LIST of bytes + */ + +int +WriteProxyFileEntry (proxyFile, theWindow) + +FILE *proxyFile; +WinInfo *theWindow; + +{ + int i; + + if (!write_counted_string (proxyFile, theWindow->client_id)) + return 0; + if (!write_counted_string (proxyFile, theWindow->class.res_name)) + return 0; + if (!write_counted_string (proxyFile, theWindow->class.res_class)) + return 0; + if (!write_counted_string (proxyFile, theWindow->wm_name)) + return 0; + + if (!theWindow->wm_command || theWindow->wm_command_count == 0) + { + if (!write_byte (proxyFile, 0)) + return 0; + } + else + { + if (!write_byte (proxyFile, (char) theWindow->wm_command_count)) + return 0; + for (i = 0; i < theWindow->wm_command_count; i++) + if (!write_counted_string (proxyFile, theWindow->wm_command[i])) + return 0; + } + + return 1; +} + + +int +ReadProxyFileEntry (proxyFile, pentry) + +FILE *proxyFile; +ProxyFileEntry **pentry; + +{ + ProxyFileEntry *entry; + char byte; + int i; + + *pentry = entry = (ProxyFileEntry *) malloc ( + sizeof (ProxyFileEntry)); + if (!*pentry) + return 0; + + entry->tag = 0; + entry->client_id = NULL; + entry->class.res_name = NULL; + entry->class.res_class = NULL; + entry->wm_name = NULL; + entry->wm_command = NULL; + entry->wm_command_count = 0; + + if (!read_counted_string (proxyFile, &entry->client_id)) + goto give_up; + if (!read_counted_string (proxyFile, &entry->class.res_name)) + goto give_up; + if (!read_counted_string (proxyFile, &entry->class.res_class)) + goto give_up; + if (!read_counted_string (proxyFile, &entry->wm_name)) + goto give_up; + + if (!read_byte (proxyFile, &byte)) + goto give_up; + entry->wm_command_count = byte; + + if (entry->wm_command_count == 0) + entry->wm_command = NULL; + else + { + entry->wm_command = (char **) malloc (entry->wm_command_count * + sizeof (char *)); + + if (!entry->wm_command) + goto give_up; + + for (i = 0; i < entry->wm_command_count; i++) + if (!read_counted_string (proxyFile, &entry->wm_command[i])) + goto give_up; + } + + return 1; + +give_up: + + if (entry->client_id) + free (entry->client_id); + if (entry->class.res_name) + free (entry->class.res_name); + if (entry->class.res_class) + free (entry->class.res_class); + if (entry->wm_name) + free (entry->wm_name); + if (entry->wm_command_count) + { + for (i = 0; i < entry->wm_command_count; i++) + if (entry->wm_command[i]) + free (entry->wm_command[i]); + } + if (entry->wm_command) + free ((char *) entry->wm_command); + + free ((char *) entry); + *pentry = NULL; + + return 0; +} + + +void +ReadProxyFile (filename) + +char *filename; + +{ + FILE *proxyFile; + ProxyFileEntry *entry; + int done = 0; + unsigned short version; + + proxyFile = fopen (filename, "rb"); + if (!proxyFile) + return; + + if (!read_short (proxyFile, &version) || + version > SAVEFILE_VERSION) + { + done = 1; + } + + while (!done) + { + if (ReadProxyFileEntry (proxyFile, &entry)) + { + entry->next = proxyFileHead; + proxyFileHead = entry; + } + else + done = 1; + } + + fclose (proxyFile); +} + + + +static char * +unique_filename (path, prefix) + +char *path; +char *prefix; + +{ +#ifndef X_NOT_POSIX + return ((char *) tempnam (path, prefix)); +#else + char tempFile[PATH_MAX]; + char *tmp; + + sprintf (tempFile, "%s/%sXXXXXX", path, prefix); + tmp = (char *) mktemp (tempFile); + if (tmp) + { + char *ptr = (char *) malloc (strlen (tmp) + 1); + strcpy (ptr, tmp); + return (ptr); + } + else + return (NULL); +#endif +} + + + +char * +WriteProxyFile () + +{ + FILE *proxyFile = NULL; + char *filename = NULL; + char *path; + WinInfo *winptr; + Bool success = False; + + path = getenv ("SM_SAVE_DIR"); + if (!path) + { + path = getenv ("HOME"); + if (!path) + path = "."; + } + + if ((filename = unique_filename (path, ".prx")) == NULL) + goto bad; + + if (!(proxyFile = fopen (filename, "wb"))) + goto bad; + + if (!write_short (proxyFile, SAVEFILE_VERSION)) + goto bad; + + success = True; + winptr = win_head; + + while (winptr && success) + { + if (winptr->client_id) + if (!WriteProxyFileEntry (proxyFile, winptr)) + { + success = False; + break; + } + + winptr = winptr->next; + } + + bad: + + if (proxyFile) + fclose (proxyFile); + + if (success) + return (filename); + else + { + if (filename) + free (filename); + return (NULL); + } +} + + + +char * +LookupClientID (theWindow) + +WinInfo *theWindow; + +{ + ProxyFileEntry *ptr; + int found = 0; + + ptr = proxyFileHead; + while (ptr && !found) + { + if (!ptr->tag && + strcmp (theWindow->class.res_name, ptr->class.res_name) == 0 && + strcmp (theWindow->class.res_class, ptr->class.res_class) == 0 && + strcmp (theWindow->wm_name, ptr->wm_name) == 0) + { + int i; + + if (theWindow->wm_command_count == ptr->wm_command_count) + { + for (i = 0; i < theWindow->wm_command_count; i++) + if (strcmp (theWindow->wm_command[i], + ptr->wm_command[i]) != 0) + break; + + if (i == theWindow->wm_command_count) + found = 1; + } + } + + if (!found) + ptr = ptr->next; + } + + if (found) + { + ptr->tag = 1; + return (ptr->client_id); + } + else + return NULL; +} diff --git a/smproxy.c b/smproxy.c new file mode 100644 index 0000000..41ed45a --- /dev/null +++ b/smproxy.c @@ -0,0 +1,1314 @@ +/* $Xorg: smproxy.c,v 1.6 2001/02/09 02:05:35 xorgcvs Exp $ */ +/****************************************************************************** + +Copyright 1994, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + +Author: Ralph Mor, X Consortium +******************************************************************************/ + +#include "smproxy.h" + +XtAppContext appContext; +Display *disp; + +Atom wmProtocolsAtom; +Atom wmSaveYourselfAtom; +Atom wmStateAtom; +Atom smClientIdAtom; +Atom wmClientLeaderAtom; + +Bool debug = 0; + +SmcConn proxy_smcConn; +XtInputId proxy_iceInputId; +char *proxy_clientId = NULL; + +WinInfo *win_head = NULL; + +int proxy_count = 0; +int die_count = 0; + +Bool ok_to_die = 0; + +Bool caught_error = 0; + +Bool sent_save_done = 0; + +int Argc; +char **Argv; + + + +Bool +HasSaveYourself (window) + +Window window; + +{ + Atom *protocols; + int numProtocols; + int i, found; + + protocols = NULL; + + if (XGetWMProtocols (disp, window, &protocols, &numProtocols) != True) + return (False); + + found = 0; + + if (protocols != NULL) + { + for (i = 0; i < numProtocols; i++) + if (protocols[i] == wmSaveYourselfAtom) + found = 1; + + XFree (protocols); + } + + return (found); +} + + + +Bool +HasXSMPsupport (window) + +Window window; + +{ + XTextProperty tp; + Bool hasIt = 0; + + if (XGetTextProperty (disp, window, &tp, smClientIdAtom)) + { + if (tp.encoding == XA_STRING && tp.format == 8 && tp.nitems != 0) + hasIt = 1; + + if (tp.value) + XFree ((char *) tp.value); + } + + return (hasIt); +} + + + +WinInfo * +GetClientLeader (winptr) + +WinInfo *winptr; + +{ + Atom actual_type; + int actual_format; + unsigned long nitems, bytesafter; + unsigned long *datap = NULL; + WinInfo *leader_winptr = NULL; + Bool failure = 0; + + if (XGetWindowProperty (disp, winptr->window, wmClientLeaderAtom, + 0L, 1L, False, AnyPropertyType, &actual_type, &actual_format, + &nitems, &bytesafter, (unsigned char **) &datap) == Success) + { + if (actual_type == XA_WINDOW && actual_format == 32 && + nitems == 1 && bytesafter == 0) + { + Window leader_win = *((Window *) datap); + + if (!LookupWindow (leader_win, &leader_winptr, NULL)) + failure = 1; + } + + if (datap) + XFree (datap); + } + + if (failure) + { + /* The client leader was defined, but we couldn't find the window */ + + return (NULL); + } + else if (leader_winptr) + { + /* We found the real client leader */ + + return (leader_winptr); + } + else + { + /* There is no client leader defined, return this window */ + + return (winptr); + } +} + + + +char * +CheckFullyQuantifiedName (name, newstring) + +char *name; +int *newstring; + +{ + /* + * Due to a bug in Xlib (for hpux in particular), some clients + * will have a WM_CLIENT_MACHINE that is not fully quantified. + * For example, we might get "excon" instead of "excon.x.org". + * This really stinks. The best we can do is tag on our own + * domain name. + */ + + if (strchr (name, '.') != NULL) + { + *newstring = 0; + return (name); + } + else + { + char hostnamebuf[80]; + char *firstDot; + + gethostname (hostnamebuf, sizeof hostnamebuf); + firstDot = strchr (hostnamebuf, '.'); + + if (!firstDot) + { + *newstring = 0; + return (name); + } + else + { + int bytes = strlen (name) + strlen (firstDot + 1) + 2; + char *newptr; + + newptr = (char *) malloc (bytes); + sprintf (newptr, "%s.%s", name, firstDot + 1); + + *newstring = 1; + return (newptr); + } + } +} + + + +void FinishSaveYourself (winInfo, has_WM_SAVEYOURSELF) + +WinInfo *winInfo; +Bool has_WM_SAVEYOURSELF; + +{ + SmProp prop1, prop2, prop3, *props[3]; + SmPropValue prop1val, prop2val, prop3val; + int i; + + if (!winInfo->got_first_save_yourself) + { + char userId[20], restartService[80]; + char *fullyQuantifiedName; + int newstring; + + prop1.name = SmProgram; + prop1.type = SmARRAY8; + prop1.num_vals = 1; + prop1.vals = &prop1val; + prop1val.value = (SmPointer) winInfo->wm_command[0]; + prop1val.length = strlen (winInfo->wm_command[0]); + + sprintf (userId, "%d", getuid()); + prop2.name = SmUserID; + prop2.type = SmARRAY8; + prop2.num_vals = 1; + prop2.vals = &prop2val; + prop2val.value = (SmPointer) userId; + prop2val.length = strlen (userId); + + fullyQuantifiedName = CheckFullyQuantifiedName ( + (char *) winInfo->wm_client_machine.value, &newstring); + sprintf (restartService, "rstart-rsh/%s", fullyQuantifiedName); + if (newstring) + free (fullyQuantifiedName); + + prop3.name = "_XC_RestartService"; + prop3.type = SmLISTofARRAY8; + prop3.num_vals = 1; + prop3.vals = &prop3val; + prop3val.value = (SmPointer) restartService; + prop3val.length = strlen (restartService); + + props[0] = &prop1; + props[1] = &prop2; + props[2] = &prop3; + + SmcSetProperties (winInfo->smc_conn, 3, props); + + winInfo->got_first_save_yourself = 1; + } + + prop1.name = SmRestartCommand; + prop1.type = SmLISTofARRAY8; + prop1.num_vals = winInfo->wm_command_count; + + prop1.vals = (SmPropValue *) malloc ( + winInfo->wm_command_count * sizeof (SmPropValue)); + + if (!prop1.vals) + { + SmcSaveYourselfDone (winInfo->smc_conn, False); + return; + } + + for (i = 0; i < winInfo->wm_command_count; i++) + { + prop1.vals[i].value = (SmPointer) winInfo->wm_command[i]; + prop1.vals[i].length = strlen (winInfo->wm_command[i]); + } + + prop2.name = SmCloneCommand; + prop2.type = SmLISTofARRAY8; + prop2.num_vals = winInfo->wm_command_count; + prop2.vals = prop1.vals; + + props[0] = &prop1; + props[1] = &prop2; + + SmcSetProperties (winInfo->smc_conn, 2, props); + + free ((char *) prop1.vals); + + /* + * If the client doesn't support WM_SAVE_YOURSELF, we should + * return failure for the save, since we really don't know if + * the application needed to save state. + */ + + SmcSaveYourselfDone (winInfo->smc_conn, has_WM_SAVEYOURSELF); +} + + + +void +SaveYourselfCB (smcConn, clientData, saveType, shutdown, interactStyle, fast) + +SmcConn smcConn; +SmPointer clientData; +int saveType; +Bool shutdown; +int interactStyle; +Bool fast; + +{ + WinInfo *winInfo = (WinInfo *) clientData; + + if (!winInfo->has_save_yourself) + { + FinishSaveYourself (winInfo, False); + } + else + { + XClientMessageEvent saveYourselfMessage; + + + /* Send WM_SAVE_YOURSELF */ + + saveYourselfMessage.type = ClientMessage; + saveYourselfMessage.window = winInfo->window; + saveYourselfMessage.message_type = wmProtocolsAtom; + saveYourselfMessage.format = 32; + saveYourselfMessage.data.l[0] = wmSaveYourselfAtom; + saveYourselfMessage.data.l[1] = CurrentTime; + + if (XSendEvent (disp, winInfo->window, False, NoEventMask, + (XEvent *) &saveYourselfMessage)) + { + winInfo->waiting_for_update = 1; + + if (debug) + { + printf ("Sent SAVE YOURSELF to 0x%x\n", winInfo->window); + printf ("\n"); + } + } + else + { + if (debug) + { + printf ("Failed to send SAVE YOURSELF to 0x%x\n", + winInfo->window); + printf ("\n"); + } + } + } +} + + + +void +DieCB (smcConn, clientData) + +SmcConn smcConn; +SmPointer clientData; + +{ + WinInfo *winInfo = (WinInfo *) clientData; + + SmcCloseConnection (winInfo->smc_conn, 0, NULL); + winInfo->smc_conn = NULL; + XtRemoveInput (winInfo->input_id); + + /* Now tell the client to die */ + + if (debug) + printf ("Trying to kill 0x%x\n", winInfo->window); + + XSync (disp, 0); + XKillClient (disp, winInfo->window); + XSync (disp, 0); + + + /* + * Proxy must exit when all clients die, and the proxy itself + * must have received a Die. + */ + + die_count++; + + if (die_count == proxy_count && ok_to_die) + { + exit (0); + } +} + + + +void +SaveCompleteCB (smcConn, clientData) + +SmcConn smcConn; +SmPointer clientData; + +{ + /* + * Nothing to do here. + */ +} + + + +void +ShutdownCancelledCB (smcConn, clientData) + +SmcConn smcConn; +SmPointer clientData; + +{ + /* + * Since we did not request to interact or request save yourself + * phase 2, we know we already sent the save yourself done, so + * there is nothing to do here. + */ +} + + + +void +ProcessIceMsgProc (client_data, source, id) + +XtPointer client_data; +int *source; +XtInputId *id; + +{ + IceConn ice_conn = (IceConn) client_data; + + IceProcessMessages (ice_conn, NULL, NULL); +} + + + +void +NullIceErrorHandler (iceConn, swap, + offendingMinorOpcode, offendingSequence, errorClass, severity, values) + +IceConn iceConn; +Bool swap; +int offendingMinorOpcode; +unsigned long offendingSequence; +int errorClass; +int severity; +IcePointer values; + +{ + return; +} + + +void +ConnectClientToSM (winInfo) + +WinInfo *winInfo; + +{ + char errorMsg[256]; + unsigned long mask; + SmcCallbacks callbacks; + IceConn ice_conn; + char *prevId; + + mask = SmcSaveYourselfProcMask | SmcDieProcMask | + SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask; + + callbacks.save_yourself.callback = SaveYourselfCB; + callbacks.save_yourself.client_data = (SmPointer) winInfo; + + callbacks.die.callback = DieCB; + callbacks.die.client_data = (SmPointer) winInfo; + + callbacks.save_complete.callback = SaveCompleteCB; + callbacks.save_complete.client_data = (SmPointer) winInfo; + + callbacks.shutdown_cancelled.callback = ShutdownCancelledCB; + callbacks.shutdown_cancelled.client_data = (SmPointer) winInfo; + + prevId = LookupClientID (winInfo); + + /* + * In case a protocol error occurs when opening the connection, + * (e.g. an authentication error), we set a null error handler + * before the open, then restore the default handler after the open. + */ + + IceSetErrorHandler (NullIceErrorHandler); + + winInfo->smc_conn = SmcOpenConnection ( + NULL, /* use SESSION_MANAGER env */ + (SmPointer) winInfo, /* force a new connection */ + SmProtoMajor, + SmProtoMinor, + mask, + &callbacks, + prevId, + &winInfo->client_id, + 256, errorMsg); + + IceSetErrorHandler (NULL); + + if (winInfo->smc_conn == NULL) + return; + + ice_conn = SmcGetIceConnection (winInfo->smc_conn); + + winInfo->input_id = XtAppAddInput ( + appContext, + IceConnectionNumber (ice_conn), + (XtPointer) XtInputReadMask, + ProcessIceMsgProc, + (XtPointer) ice_conn); + + if (debug) + { + printf ("Connected to SM, window = 0x%x\n", winInfo->window); + printf ("\n"); + } + + proxy_count++; +} + + + +MyErrorHandler (display, event) + +Display *display; +XErrorEvent *event; + +{ + caught_error = 1; +} + + + +Bool +LookupWindow (window, ptr_ret, prev_ptr_ret) + +Window window; +WinInfo **ptr_ret; +WinInfo **prev_ptr_ret; + +{ + WinInfo *ptr, *prev; + + ptr = win_head; + prev = NULL; + + while (ptr) + { + if (ptr->window == window) + break; + else + { + prev = ptr; + ptr = ptr->next; + } + } + + if (ptr) + { + if (ptr_ret) + *ptr_ret = ptr; + if (prev_ptr_ret) + *prev_ptr_ret = prev; + return (1); + } + else + return (0); +} + + + +WinInfo * +AddNewWindow (window) + +Window window; + +{ + WinInfo *newptr; + + if (LookupWindow (window, NULL, NULL)) + return (NULL); + + newptr = (WinInfo *) malloc (sizeof (WinInfo)); + + if (newptr == NULL) + return (NULL); + + newptr->next = win_head; + win_head = newptr; + + newptr->window = window; + newptr->smc_conn = NULL; + newptr->tested_for_sm_client_id = 0; + newptr->client_id = NULL; + newptr->wm_command = NULL; + newptr->wm_command_count = 0; + newptr->class.res_name = NULL; + newptr->class.res_class = NULL; + newptr->wm_name = NULL; + newptr->wm_client_machine.value = NULL; + newptr->wm_client_machine.nitems = 0; + newptr->has_save_yourself = 0; + newptr->waiting_for_update = 0; + newptr->got_first_save_yourself = 0; + + return (newptr); +} + + + +void +RemoveWindow (winptr) + +WinInfo *winptr; + +{ + WinInfo *ptr, *prev; + + if (LookupWindow (winptr->window, &ptr, &prev)) + { + if (prev == NULL) + win_head = ptr->next; + else + prev->next = ptr->next; + + if (ptr->client_id) + free (ptr->client_id); + + if (ptr->wm_command) + XFreeStringList (ptr->wm_command); + + if (ptr->wm_name) + XFree (ptr->wm_name); + + if (ptr->wm_client_machine.value) + XFree (ptr->wm_client_machine.value); + + if (ptr->class.res_name) + XFree (ptr->class.res_name); + + if (ptr->class.res_class) + XFree (ptr->class.res_class); + + free ((char *) ptr); + } +} + + + +void +Got_WM_STATE (winptr) + +WinInfo *winptr; + +{ + WinInfo *leader_winptr; + + /* + * If we already got WM_STATE and tested for SM_CLIENT_ID, we + * shouldn't do it again. + */ + + if (winptr->tested_for_sm_client_id) + { + return; + } + + + /* + * Set a null error handler, in case this window goes away + * behind our back. + */ + + caught_error = 0; + XSetErrorHandler (MyErrorHandler); + + + /* + * Get the client leader window. + */ + + leader_winptr = GetClientLeader (winptr); + + if (caught_error) + { + caught_error = 0; + RemoveWindow (winptr); + XSetErrorHandler (NULL); + return; + } + + + /* + * If we already checked for SM_CLIENT_ID on the client leader + * window, don't do it again. + */ + + if (!leader_winptr || leader_winptr->tested_for_sm_client_id) + { + caught_error = 0; + XSetErrorHandler (NULL); + return; + } + + leader_winptr->tested_for_sm_client_id = 1; + + if (!HasXSMPsupport (leader_winptr->window)) + { + XFetchName (disp, leader_winptr->window, &leader_winptr->wm_name); + + XGetCommand (disp, leader_winptr->window, + &leader_winptr->wm_command, + &leader_winptr->wm_command_count); + + XGetClassHint (disp, leader_winptr->window, &leader_winptr->class); + + XGetWMClientMachine (disp, leader_winptr->window, + &leader_winptr->wm_client_machine); + + if (leader_winptr->wm_name != NULL && + leader_winptr->wm_command != NULL && + leader_winptr->wm_command_count > 0 && + leader_winptr->class.res_name != NULL && + leader_winptr->class.res_class != NULL && + leader_winptr->wm_client_machine.value != NULL && + leader_winptr->wm_client_machine.nitems != 0) + { + leader_winptr->has_save_yourself = + HasSaveYourself (leader_winptr->window); + + ConnectClientToSM (leader_winptr); + } + } + + XSync (disp, 0); + XSetErrorHandler (NULL); + + if (caught_error) + { + caught_error = 0; + RemoveWindow (leader_winptr); + } +} + + + +void +HandleCreate (event) + +XCreateWindowEvent *event; + +{ + Atom actual_type; + int actual_format; + unsigned long nitems, bytesafter; + unsigned long *datap = NULL; + WinInfo *winptr; + Bool got_wm_state = 0; + + /* + * We are waiting for all proxy connections to close so we can die. + * Don't handle new connections. + */ + + if (ok_to_die) + return; + + + /* + * Add the new window + */ + + if ((winptr = AddNewWindow (event->window)) == NULL) + return; + + + /* + * Right after the window was created, it might have been destroyed, + * so the following Xlib calls might fail. Need to catch the error + * by installing an error handler. + */ + + caught_error = 0; + XSetErrorHandler (MyErrorHandler); + + + /* + * Select for Property Notify on the window so we can determine + * when WM_STATE is defined. To avoid a race condition, we must + * do this _before_ we check for WM_STATE right now. + * + * Select for Substructure Notify so we can determine when the + * window is destroyed. + */ + + XSelectInput (disp, event->window, + SubstructureNotifyMask | PropertyChangeMask); + + + /* + * WM_STATE may already be there. Check now. + */ + + if (XGetWindowProperty (disp, event->window, wmStateAtom, + 0L, 2L, False, AnyPropertyType, + &actual_type, &actual_format, &nitems, &bytesafter, + (unsigned char **) &datap) == Success && datap) + { + if (nitems > 0) + got_wm_state = 1; + + if (datap) + XFree ((char *) datap); + } + + XSync (disp, 0); + XSetErrorHandler (NULL); + + if (caught_error) + { + caught_error = 0; + RemoveWindow (winptr); + } + else if (got_wm_state) + { + Got_WM_STATE (winptr); + } +} + + + +void +HandleDestroy (event) + +XDestroyWindowEvent *event; + +{ + WinInfo *winptr; + + if (LookupWindow (event->window, &winptr, NULL)) + { + if (winptr->smc_conn) + { + SmcCloseConnection (winptr->smc_conn, 0, NULL); + XtRemoveInput (winptr->input_id); + proxy_count--; + } + + if (debug) + { + printf ("Removed window (window = 0x%x)\n", winptr->window); + printf ("\n"); + } + + RemoveWindow (winptr); + } +} + + + +void +HandleUpdate (event) + +XPropertyEvent *event; + +{ + Window window = event->window; + WinInfo *winptr; + + if (!LookupWindow (window, &winptr, NULL)) + return; + + if (event->atom == wmStateAtom) + { + Got_WM_STATE (winptr); + } + else if (event->atom == XA_WM_COMMAND && winptr->waiting_for_update) + { + /* Finish off the Save Yourself */ + + if (winptr->wm_command) + { + XFreeStringList (winptr->wm_command); + winptr->wm_command = NULL; + winptr->wm_command_count = 0; + } + + XGetCommand (disp, window, + &winptr->wm_command, + &winptr->wm_command_count); + + winptr->waiting_for_update = 0; + FinishSaveYourself (winptr, True); + } +} + + + +void +ProxySaveYourselfPhase2CB (smcConn, clientData) + +SmcConn smcConn; +SmPointer clientData; + +{ + char *filename; + Bool success = True; + SmProp prop1, prop2, prop3, *props[3]; + SmPropValue prop1val, prop2val, prop3val; + char discardCommand[80]; + int numVals, i; + static int first_time = 1; + + if (first_time) + { + char userId[20]; + char hint = SmRestartIfRunning; + + prop1.name = SmProgram; + prop1.type = SmARRAY8; + prop1.num_vals = 1; + prop1.vals = &prop1val; + prop1val.value = Argv[0]; + prop1val.length = strlen (Argv[0]); + + sprintf (userId, "%d", getuid()); + prop2.name = SmUserID; + prop2.type = SmARRAY8; + prop2.num_vals = 1; + prop2.vals = &prop2val; + prop2val.value = (SmPointer) userId; + prop2val.length = strlen (userId); + + prop3.name = SmRestartStyleHint; + prop3.type = SmCARD8; + prop3.num_vals = 1; + prop3.vals = &prop3val; + prop3val.value = (SmPointer) &hint; + prop3val.length = 1; + + props[0] = &prop1; + props[1] = &prop2; + props[2] = &prop3; + + SmcSetProperties (smcConn, 3, props); + + first_time = 0; + } + + if ((filename = WriteProxyFile ()) == NULL) + { + success = False; + goto finishUp; + } + + prop1.name = SmRestartCommand; + prop1.type = SmLISTofARRAY8; + + prop1.vals = (SmPropValue *) malloc ( + (Argc + 4) * sizeof (SmPropValue)); + + if (!prop1.vals) + { + success = False; + goto finishUp; + } + + numVals = 0; + + for (i = 0; i < Argc; i++) + { + if (strcmp (Argv[i], "-clientId") == 0 || + strcmp (Argv[i], "-restore") == 0) + { + i++; + } + else + { + prop1.vals[numVals].value = (SmPointer) Argv[i]; + prop1.vals[numVals++].length = strlen (Argv[i]); + } + } + + prop1.vals[numVals].value = (SmPointer) "-clientId"; + prop1.vals[numVals++].length = 9; + + prop1.vals[numVals].value = (SmPointer) proxy_clientId; + prop1.vals[numVals++].length = strlen (proxy_clientId); + + prop1.vals[numVals].value = (SmPointer) "-restore"; + prop1.vals[numVals++].length = 8; + + prop1.vals[numVals].value = (SmPointer) filename; + prop1.vals[numVals++].length = strlen (filename); + + prop1.num_vals = numVals; + + + sprintf (discardCommand, "rm %s", filename); + prop2.name = SmDiscardCommand; + prop2.type = SmARRAY8; + prop2.num_vals = 1; + prop2.vals = &prop2val; + prop2val.value = (SmPointer) discardCommand; + prop2val.length = strlen (discardCommand); + + props[0] = &prop1; + props[1] = &prop2; + + SmcSetProperties (smcConn, 2, props); + free ((char *) prop1.vals); + + finishUp: + + SmcSaveYourselfDone (smcConn, success); + sent_save_done = 1; + + if (filename) + free (filename); +} + + + +void +ProxySaveYourselfCB (smcConn, clientData, saveType, + shutdown, interactStyle, fast) + +SmcConn smcConn; +SmPointer clientData; +int saveType; +Bool shutdown; +int interactStyle; +Bool fast; + +{ + /* + * We want the proxy to respond to the Save Yourself after all + * the regular XSMP clients have finished with the save (and possibly + * interacted with the user). + */ + + if (!SmcRequestSaveYourselfPhase2 (smcConn, + ProxySaveYourselfPhase2CB, NULL)) + { + SmcSaveYourselfDone (smcConn, False); + sent_save_done = 1; + } + else + sent_save_done = 0; +} + + + +void +ProxyDieCB (smcConn, clientData) + +SmcConn smcConn; +SmPointer clientData; + +{ + SmcCloseConnection (proxy_smcConn, 0, NULL); + XtRemoveInput (proxy_iceInputId); + + if (die_count == proxy_count) + exit (0); + else + ok_to_die = 1; +} + + + +void +ProxySaveCompleteCB (smcConn, clientData) + +SmcConn smcConn; +SmPointer clientData; + +{ + ; +} + + + +void +ProxyShutdownCancelledCB (smcConn, clientData) + +SmcConn smcConn; +SmPointer clientData; + +{ + if (!sent_save_done) + { + SmcSaveYourselfDone (smcConn, False); + sent_save_done = 1; + } +} + + + +Status +ConnectProxyToSM (previous_id) + +char *previous_id; + +{ + char errorMsg[256]; + unsigned long mask; + SmcCallbacks callbacks; + IceConn iceConn; + + mask = SmcSaveYourselfProcMask | SmcDieProcMask | + SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask; + + callbacks.save_yourself.callback = ProxySaveYourselfCB; + callbacks.save_yourself.client_data = (SmPointer) NULL; + + callbacks.die.callback = ProxyDieCB; + callbacks.die.client_data = (SmPointer) NULL; + + callbacks.save_complete.callback = ProxySaveCompleteCB; + callbacks.save_complete.client_data = (SmPointer) NULL; + + callbacks.shutdown_cancelled.callback = ProxyShutdownCancelledCB; + callbacks.shutdown_cancelled.client_data = (SmPointer) NULL; + + proxy_smcConn = SmcOpenConnection ( + NULL, /* use SESSION_MANAGER env */ + (SmPointer) appContext, + SmProtoMajor, + SmProtoMinor, + mask, + &callbacks, + previous_id, + &proxy_clientId, + 256, errorMsg); + + if (proxy_smcConn == NULL) + return (0); + + iceConn = SmcGetIceConnection (proxy_smcConn); + + proxy_iceInputId = XtAppAddInput ( + appContext, + IceConnectionNumber (iceConn), + (XtPointer) XtInputReadMask, + ProcessIceMsgProc, + (XtPointer) iceConn); + + return (1); +} + + + +void +CheckForExistingWindows (root) + +Window root; + +{ + Window dontCare1, dontCare2, *children, client_window; + unsigned int nchildren, i; + XCreateWindowEvent event; + + /* + * We query the root tree for all windows created thus far. + * Note that at any moment after XQueryTree is called, a + * window may be deleted. So we must take extra care to make + * sure a window really exists. + */ + + XQueryTree (disp, root, &dontCare1, &dontCare2, &children, &nchildren); + + for (i = 0; i < nchildren; i++) + { + event.window = children[i]; + + HandleCreate (&event); + + caught_error = 0; + XSetErrorHandler (MyErrorHandler); + + client_window = XmuClientWindow (disp, children[i]); + + XSetErrorHandler (NULL); + + if (!caught_error && client_window != children[i]) + { + event.window = client_window; + HandleCreate (&event); + } + } +} + + + +main (argc, argv) + +int argc; +char **argv; + +{ + char *restore_filename = NULL; + char *client_id = NULL; + int i, zero = 0; + + Argc = argc; + Argv = argv; + + for (i = 1; i < argc; i++) + { + if (argv[i][0] == '-') + { + switch (argv[i][1]) + { + case 'd': /* -debug */ + debug = 1; + continue; + + case 'c': /* -clientId */ + if (++i >= argc) goto usage; + client_id = argv[i]; + continue; + + case 'r': /* -restore */ + if (++i >= argc) goto usage; + restore_filename = argv[i]; + continue; + } + } + + usage: + + fprintf (stderr, + "usage: %s [-clientId id] [-restore file] [-debug]\n", argv[0]); + exit (1); + } + + + XtToolkitInitialize (); + appContext = XtCreateApplicationContext (); + + if (!(disp = XtOpenDisplay (appContext, NULL, "SM-PROXY", "SM-PROXY", + NULL, 0, &zero, NULL))) + { + fprintf (stderr, "smproxy: unable to open display\n"); + exit (1); + } + + if (restore_filename) + ReadProxyFile (restore_filename); + + if (!ConnectProxyToSM (client_id)) + { + fprintf (stderr, "smproxy: unable to connect to session manager\n"); + exit (1); + } + + wmProtocolsAtom = XInternAtom (disp, "WM_PROTOCOLS", False); + wmSaveYourselfAtom = XInternAtom (disp, "WM_SAVE_YOURSELF", False); + wmStateAtom = XInternAtom (disp, "WM_STATE", False); + smClientIdAtom = XInternAtom (disp, "SM_CLIENT_ID", False); + wmClientLeaderAtom = XInternAtom (disp, "WM_CLIENT_LEADER", False); + + for (i = 0; i < ScreenCount (disp); i++) + { + Window root = RootWindow (disp, i); + XSelectInput (disp, root, SubstructureNotifyMask | PropertyChangeMask); + CheckForExistingWindows (root); + } + + while (1) + { + XEvent event; + + XtAppNextEvent (appContext, &event); + + switch (event.type) + { + case CreateNotify: + HandleCreate (&event.xcreatewindow); + break; + + case DestroyNotify: + HandleDestroy (&event.xdestroywindow); + break; + + case PropertyNotify: + HandleUpdate (&event.xproperty); + break; + + default: + XtDispatchEvent (&event); + break; + } + } +} diff --git a/smproxy.h b/smproxy.h new file mode 100644 index 0000000..a6e7522 --- /dev/null +++ b/smproxy.h @@ -0,0 +1,101 @@ +/* $Xorg: smproxy.h,v 1.4 2001/02/09 02:05:36 xorgcvs Exp $ */ +/****************************************************************************** + +Copyright 1994, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + +Author: Ralph Mor, X Consortium +******************************************************************************/ + +#include <X11/Xosdefs.h> +#include <X11/Xfuncs.h> + +#include <X11/StringDefs.h> +#include <X11/Intrinsic.h> +#include <X11/Xatom.h> +#include <X11/Xlib.h> +#include <X11/SM/SMlib.h> + +#ifndef _POSIX_SOURCE +#define _POSIX_SOURCE +#include <stdio.h> +#undef _POSIX_SOURCE +#else +#include <stdio.h> +#endif + +#ifndef X_NOT_STDC_ENV +#include <stdlib.h> +#else +extern char *malloc(), *calloc(), *realloc(), *getenv(); +extern void free(); +#endif + +#ifndef X_NOT_POSIX +#ifdef _POSIX_SOURCE +#include <limits.h> +#else +#define _POSIX_SOURCE +#include <limits.h> +#undef _POSIX_SOURCE +#endif +#endif /* X_NOT_POSIX */ +#ifndef PATH_MAX +#include <sys/param.h> +#ifndef PATH_MAX +#ifdef MAXPATHLEN +#define PATH_MAX MAXPATHLEN +#else +#define PATH_MAX 1024 +#endif +#endif +#endif /* PATH_MAX */ + + +typedef struct WinInfo { + Window window; + SmcConn smc_conn; + XtInputId input_id; + char *client_id; + char **wm_command; + int wm_command_count; + XClassHint class; + char *wm_name; + XTextProperty wm_client_machine; + struct WinInfo *next; + + unsigned int tested_for_sm_client_id : 1; + unsigned int has_save_yourself : 1; + unsigned int waiting_for_update : 1; + unsigned int got_first_save_yourself : 1; + +} WinInfo; + + +extern int WriteProxyFileEntry (); +extern int ReadProxyFileEntry (); +extern void ReadProxyFile (); +extern char *WriteProxyFile (); +extern char *LookupClientID (); + + +#define SAVEFILE_VERSION 1 diff --git a/smproxy.man b/smproxy.man new file mode 100644 index 0000000..0c1c355 --- /dev/null +++ b/smproxy.man @@ -0,0 +1,72 @@ +.\" $Xorg: smproxy.man,v 1.4 2001/02/09 02:05:36 xorgcvs Exp $ +.\" Copyright 1994, 1998 The Open Group +.\" +.\" Permission to use, copy, modify, distribute, and sell this software and its +.\" documentation for any purpose is hereby granted without fee, provided that +.\" the above copyright notice appear in all copies and that both that +.\" copyright notice and this permission notice appear in supporting +.\" documentation. +.\" +.\" The above copyright notice and this permission notice shall be included +.\" in all copies or substantial portions of the Software. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +.\" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +.\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +.\" IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR +.\" OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +.\" ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +.\" OTHER DEALINGS IN THE SOFTWARE. +.\" +.\" Except as contained in this notice, the name of The Open Group shall +.\" not be used in advertising or otherwise to promote the sale, use or +.\" other dealings in this Software without prior written authorization +.\" from The Open Group. +.TH XSM 1 "Release 6.4" "X Version 11" +.SH NAME +smproxy \- Session Manager Proxy +.SH SYNOPSIS +.B smproxy +[-clientId id] [-restore saveFile] +.SH OPTIONS +.TP 8 +.B \-clientId \fIid\fP +Specifies the session ID used by \fIsmproxy\fP in the previous session. +.TP 8 +.B \-restore \fIsaveFile\fP +Specifies the file used by \fIsmproxy\fP to save state in the previous session. +.SH DESCRIPTION +.PP +\fIsmproxy\fP allows X applications that do not support X11R6 session +management to participate in an X11R6 session. +.br +.sp +In order for \fIsmproxy\fP to act as a proxy for an X application, one of +the following must be true: +.br +.sp +- The application maps a top level window containing the +\fBWM_CLIENT_LEADER\fR property. This property provides a pointer to +the client leader window which contains the \fBWM_CLASS\fR, \fBWM_NAME\fR, +\fBWM_COMMAND\fR, and \fBWM_CLIENT_MACHINE\fR properties. +.br +.sp +or ... +.br +.sp +- The application maps a top level window which does not contain the +\fBWM_CLIENT_LEADER\fR property. However, this top level window +contains the \fBWM_CLASS\fR, \fBWM_NAME\fR, \fBWM_COMMAND\fR, and +\fBWM_CLIENT_MACHINE\fR properties. +.PP +An application that support the \fBWM_SAVE_YOURSELF\fR protocol will receive +a \fBWM_SAVE_YOURSELF\fR client message each time the session manager issues +a checkpoint or shutdown. This allows the application to save state. If +an application does not support the \fBWM_SAVE_YOURSELF\fR protocol, then +the proxy will provide enough information to the session manager to restart +the application (using \fBWM_COMMAND\fR), but no state will be restored. +.SH SEE ALSO +xsm(1) +.SH AUTHOR +Ralph Mor, X Consortium + |