Syncing a Tungsten T via USB using gpilotd works



Hi all,
 
 
I am using a Palm Tungsten T (running PalmOS5.0) with
RedHat8.0 (2.4.18-18), pilot-link-0.11.7, gnome-pilot-0.1.71-1
and evolution 1.2.1. This configuration is not able
to sync the Tungsten T using the USB craddle.

I digged through the gnome-pilot sources, managed to
patch the sources and now I am a happy syncer. :-)

I have attached the file gpilotd.c which contains my
(ugly) patch for the Tungsten T.

The gory details:

I am checking the output of /proc/bus/usb/devices against the unique
USB product ID of the Tungsten T (Vendor=0830 ProdID=0060).
If this string is found, we can set the necessary parameters/flags
for syncing.


Cheers,
Peter
-- 
Suedwest Presse Online-Dienste GmbH 
Dipl.-Inform.(FH) Peter Dieth - Geschäftsbereichsleiter 
Frauenstrasse 77, D-89073 Ulm 
peter dieth suedwest-aktiv de   http://www.suedwest-aktiv.de
Fon: 0731-156-801  Fax: 0731-156-659 
http://www.koepfe.de/me.asp?ID=PRDH207303
PGP key http://www.dieth.de/contact.phtml
PGP fingerprint 01 98 7D CB 6D 84 C1 35 59 32 CE 5C 0A 33 E8 88 
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- *//* 
 * Copyright (C) 1998-2000 Free Software Foundation
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 * Authors: Eskil Heyn Olsen
 *          Vadim Strizhevsky
 *          Manish Vachharajani
 *          Dave Camp
 *
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

/* for crypt () */
#ifdef USE_XOPEN_SOURCE
#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE 
#endif /* _XOPEN_SOURCE  */
#endif

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/time.h>

#include <glib.h>
#include <pwd.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>

#include <libgnome/libgnome.h>

#include <pi-source.h>
#include <pi-dlp.h>
#include <pi-version.h>

#include "manager.h"
#include "gnome-pilot-structures.h"
#include "orbit_daemon_glue.h"
#include "gpilot-gui.h"
#include "gnome-pilot-conduit-backup.h"

#include <libgpilotdCM/gnome-pilot-conduit-management.h>

/* Set to true when the config should be reloaded */
gboolean reread_config;

gint device_equal_by_io (GPilotDevice *,GIOChannel *);
gboolean device_in (GIOChannel *,
		   GIOCondition ,
		   GPilotContext *);
gboolean device_err (GIOChannel *,
		    GIOCondition ,
		    GPilotContext *);
gboolean network_device_in (GIOChannel *,
		   GIOCondition ,
		   GPilotContext *);
gboolean network_device_err (GIOChannel *,
		    GIOCondition ,
		    GPilotContext *);
void monitor_channel (GPilotDevice *,GPilotContext *);
void remove_pid_file (void);

gint 
device_equal_by_io (GPilotDevice *dev,GIOChannel *io) 
{
	return !(dev->io==io);
}

static void
remove_device (GPilotContext *context,GPilotDevice *device) 
{
	GList *l;

	g_message ("Removing %s",device->name);

	l = g_list_find (context->devices,device);
	if (l != NULL) {
		gpilot_device_free (l->data);
		g_list_remove (context->devices,l);
	} else {
		g_message ("%s not found",device->name);
	}
}

static void 
pilot_set_baud_rate (GPilotDevice *device) 
{
	static gchar rate_buf[128];
	g_snprintf (rate_buf,128,"PILOTRATE=%d", device->speed);
	g_message ("setting %s",rate_buf);
	putenv (rate_buf);
}

/*
  Sets *error to 1 on fatal error on the device, 2 on other errors , 0 otherwise.
 */
static int 
pilot_connect (GPilotDevice *device,int *error) 
{
	struct pi_sockaddr addr;
	int sd, listen_sd, pf;
	int ret;
	
	if (device->type != PILOT_DEVICE_NETWORK) {
		pilot_set_baud_rate (device);
	}

	switch (device->type) {
	case PILOT_DEVICE_SERIAL:
		pf = PI_PF_PADP;
		break;
	case PILOT_DEVICE_USB_VISOR:
		pf = PI_PF_NET;
		break;
	case PILOT_DEVICE_IRDA:
		pf = PI_PF_PADP;
		break;
	case PILOT_DEVICE_NETWORK:
		pf = PI_PF_NET;
		break;
	default:
		pf = PI_PF_DLP;
		break;
	}
	
	if (!(listen_sd = pi_socket (PI_AF_PILOT, PI_SOCK_STREAM, pf))) {
		g_warning ("pi_socket: %s",strerror (errno));
		if (error) *error = 1;
		return -1;
	}
	
	addr.pi_family = PI_AF_PILOT;

/*
	Most important for networking
	. resolves inside libpisock as network
	It is done earlier in gpilotd_device_init
	so don't really need to do it again here.
*/
	if (device->type == PILOT_DEVICE_NETWORK) {
		device->port = "net:any";
	}

	strcpy (addr.pi_device,device->port);

	ret = pi_bind (listen_sd, (struct sockaddr*)&addr, sizeof (addr));
	if (ret == -1) {
		g_warning (_("Unable to bind to pilot"));
		if (error)
			*error = 1;
		pi_close(listen_sd);
		return 0;
	}

	ret = pi_listen (listen_sd, 1);
	if (ret != 0) {
		g_warning ("pi_listen: %s", strerror (errno));
		if (error)
			*error = 2;
		pi_close(listen_sd);
		return 0;
	}

	sd = pi_accept_to (listen_sd, NULL,0, device->timeout); 
	if (sd == -1) {
		g_warning ("pi_accept_to: %s", strerror (errno));
		g_warning ("pi_accept_to: timeout was %d secs", device->timeout);
		if (error)
			*error = 2;
		pi_close(listen_sd);
		return 0;
	}
		
	if (error)
		*error = 0;
	pi_close(listen_sd);
	
	return sd;
}

static void pilot_disconnect (int sd)
{
	dlp_EndOfSync (sd, 0);
	pi_close (sd);
}

static void write_sync_stamp (GPilotPilot *pilot,
			     int pfd,
			     struct PilotUser *pu,
			     guint32 last_sync_pc, 
			     time_t t)
{
	gchar prefix[256];

	pu->lastSyncPC=last_sync_pc;
	pu->lastSyncDate=t;

	g_snprintf (prefix,255,"/gnome-pilot.d/gpilotd/Pilot%d/",pilot->number);
	gnome_config_push_prefix (prefix);
	gnome_config_private_set_int ("sync_date",t);
	gnome_config_pop_prefix ();
	gnome_config_sync ();

	dlp_WriteUserInfo (pfd,pu);
}

/** pilot lookup methods **/

static gint
match_pilot_and_majick (const GPilotPilot *pilot,
			unsigned long creation,
			unsigned long romversion)
{
	if (pilot->creation == creation &&
	    pilot->romversion == romversion) {
		g_message ("pilot %s %ld %ld matches %ld %ld", 
			   pilot->name, 
			   pilot->creation,
			   pilot->romversion, 
			   creation, 
			   romversion);
		return 1;
	}
	g_message ("pilot %s %ld %ld doesn't match %ld %ld", 
		   pilot->name, 
		   pilot->creation,
		   pilot->romversion, 
		   creation, 
		   romversion);
	return 0;
}

/**************************/

/*
 * We get a pfd, a device and the context (called if a pilot
 * with id == 0 synchronizes)
 * Get whatever majick number we can get and try to id 
 * the pilot and offer to restore it. If it can id the
 * pilot, ask the user to choose one or "make a new pilot"
 */

static gboolean
gpilot_attempt_restore (struct PilotUser pu,
			int pfd, 
			GPilotDevice *device, 
			GPilotContext *context)
{
	struct SysInfo sysinfo;
	struct CardInfo cardinfo;
	GPilotPilot *pilot = NULL;
	GList *iterator;
	gboolean result = FALSE;
	
	dlp_ReadStorageInfo (pfd, 0, &cardinfo);
	dlp_ReadSysInfo (pfd, &sysinfo);

	if (g_list_length (context->pilots) == 1) {
		pilot = GPILOT_PILOT (g_list_nth (context->pilots, 0)->data);
		g_message ("D: Only one pilot (%s) profile...", pilot->name);
	} else {
		for (iterator = context->pilots; iterator; iterator = g_list_next (iterator)) {
			pilot = GPILOT_PILOT (iterator->data);
			if (match_pilot_and_majick (pilot, 
						    cardinfo.creation,
						    sysinfo.romVersion)) {
				break;
			}
		}
	}

	if (pilot) {
		GPilotPilot *a_pilot;
		a_pilot = gpilot_gui_restore (context, pilot);
		if (a_pilot) {
			orbed_notify_connect (pilot->name,pu);				
			result = gpilot_start_unknown_restore (pfd, device, a_pilot);
			orbed_notify_disconnect (pilot->name);
		}
	} else {
		/* MUST GO */
		gpilot_gui_warning_dialog ("no ident\n"
					   "restoring pilot with ident\n"
					   "c/r = %lu/%lu, exciting things\n"
					   "will soon be here...",
					   cardinfo.creation,
					   sysinfo.romVersion);
	}

	return TRUE;
}

/*
 * This function handles when sync_device (...) encounters an unknown pilot
 */

 static void
gpilot_syncing_unknown_pilot (struct PilotUser pu, 
			      int pfd,
			      GPilotDevice *device, 
			      GPilotContext *context)
{

	g_warning (_("Unknown pilot, no userID/username match %ld"),pu.userID);
	/* FIXME: here, restoring one of the available pilots should be
	   offered to the user. Of course with password prompt if the user
	   has password set
	   bug # 8217 */
	if (pu.userID == 0) {
		if (gpilot_attempt_restore (pu, pfd, device, context) == FALSE) {
			gpilot_gui_warning_dialog (_("Use gnomecc to configure pilot"));
		}
	} else {
		/* FIXME: here we should offer to create a profile for the pilot,
		   bug # 8218 */
		gpilot_gui_warning_dialog (_("Unknown pilot - no pilots matches ID %ld\n"
					     "Use gnomecc to set pilot's ID"),pu.userID);
	}
}

/*
 * If there are events for the cradle, this executes them,
 * closes the connection and returns.
 * Returns TRUE if connection should be closed afterwards, FALSE
 * is sync should continue
 */
static gboolean 
do_cradle_events (int pfd,
		 GPilotContext *context,
		 struct PilotUser *pu,
		 GPilotDevice *device) 
{
	GList *events,*it;
	gboolean ret = TRUE;

	/* elements in events freed by gpc_request_purge calls
	   in orbed_notify_completion */
	events = gpc_queue_load_requests_for_cradle (device->name);

	g_message (_("Cradle %s has %d events"), device->name, g_list_length (events));

	/* if no events, return FALSE */
	if (g_list_length (events)==0) ret = FALSE;

	it = events;
	
	while (it) {
		GPilotRequest *req;
		req = it->data;
		switch (req->type) {
		case GREQ_SET_USERINFO:
			g_message (_("Setting userinfo..."));
			g_snprintf (pu->username,127,"%s", req->parameters.set_userinfo.user_id);
			pu->userID = req->parameters.set_userinfo.pilot_id;
			dlp_WriteUserInfo (pfd,pu);
			if (req->parameters.set_userinfo.continue_sync) {
				g_message (_("Sync continues"));
				ret = FALSE;
			}
			orbed_notify_completion (&req);
			break;
		case GREQ_GET_SYSINFO: {
			struct SysInfo sysinfo;
			struct CardInfo cardinfo;

			dlp_ReadStorageInfo (pfd, 0, &cardinfo);
			dlp_ReadSysInfo (pfd, &sysinfo);
			orbed_notify_sysinfo (device->name,
					      sysinfo,
					      cardinfo, 
					      &req);
			orbed_notify_completion (&req);
		}
		break;
		case GREQ_GET_USERINFO:
			g_message (_("Getting userinfo..."));
			orbed_notify_userinfo (*pu,&req);
			orbed_notify_completion (&req);
			break;
		case GREQ_NEW_USERINFO:
			/* FIXME: this is to set the new and return the old (or something) 
			   g_message ("getting & setting userinfo");
			   g_snprintf (pu->username,127,"%s",req->parameters.set_userinfo.user_id);
			   pu->userID = req->parameters.set_userinfo.pilot_id;
			   dlp_WriteUserInfo (pfd,pu);
			   orbed_notify_completion (&req);
			*/
			break;
		default:
			g_warning ("%s:%d: *** type = %d",__FILE__,__LINE__,req->type);
			g_assert_not_reached ();
			break;
		}

		it = g_list_next (it);
	}

	return ret;
}
/**************************/

/*
  This executes a sync for a pilot.

  If first does some printing to the stdout and some logging to the
  pilot, so the dudes can see what is going on. Afterwards, it does
  the initial synchronization operations (which is handling file
  installs, restores, specific conduits runs). This function (in
  manager.c) returns a boolean, whic may abort the entire
  synchronization.

  If it does not, a function in manager.c will be called depending of
  the default_sync_action setting for the pilot (you know, synchronize
  vs copy to/from blablabla).

 */
static void 
do_sync (int pfd,   
	GPilotContext *context,
	struct PilotUser *pu,
	GPilotPilot *pilot, 
	GPilotDevice *device)
{
	GList *conduit_list, *backup_conduit_list, *file_conduit_list;
	GnomePilotSyncStamp stamp;
	gchar *pilot_name;

	pilot_name = pilot_name_from_id (pu->userID,context);

	gpilot_load_conduits (context,
			     pilot,
			     &conduit_list, 
			     &backup_conduit_list,
			     &file_conduit_list);
	stamp.sync_PC_Id=context->sync_PC_Id;

	if (device->type == PILOT_DEVICE_NETWORK) {
		g_message (_("NetSync request detected, synchronizing pilot"));
	} else {
		g_message (_("HotSync button pressed, synchronizing pilot"));
	}
	g_message (_("Pilot ID is %ld, name is %s, owner is %s"),
		  pu->userID,
		  pilot->name,
		  pu->username);
  
	/* Set a log entry in the pilot */
	{
		gchar hostname[64];
		gpilot_add_log_entry (pfd,"gnome-pilot v.%s\n",VERSION);
		if (gethostname (hostname,63)==0)
			gpilot_add_log_entry (pfd,_("On host %s\n"),hostname);
		else
			gpilot_add_log_entry (pfd,_("On host %d\n"),stamp.sync_PC_Id);
	}

	/* first, run the initial operations, such as single conduit runs,
	   restores etc. If this returns True, continue with normal conduit running,
	   if False, don't proceed */
	if (gpilot_initial_synchronize_operations (pfd,
						  &stamp,
						  pu,
						  conduit_list,
						  backup_conduit_list,
						  file_conduit_list,
						  device,
						  context)) {

		/* FIXME: We need to actually pass a different structure to these
		   functions containing the pu item and the pilot pointer so that
		   we can store the lastSyncPC and assorted fields in
		   ~/.gnome_private/gnome-pilot.d/gpilotd. We will also need some
		   lock mechanism when we start forking for that file :)*/
		switch (pilot->sync_options.default_sync_action) {
		case GnomePilotConduitSyncTypeSynchronize:
			g_message (_("Synchronizing..."));
			gpilot_synchronize (pfd,&stamp,pu,
					   conduit_list,
					   backup_conduit_list,
					   file_conduit_list,
					   context);
			break;
		case GnomePilotConduitSyncTypeCopyToPilot:
			g_message (_("Copying to pilot..."));
			gpilot_copy_to_pilot (pfd,&stamp,pu,
					     conduit_list,
					     backup_conduit_list,
					     file_conduit_list,
					     context);
			break;
		case GnomePilotConduitSyncTypeCopyFromPilot:
			g_message (_("Copying from pilot..."));
			gpilot_copy_from_pilot (pfd,&stamp,pu,
					       conduit_list,
					       backup_conduit_list,
					       context);
			break;
		case GnomePilotConduitSyncTypeMergeToPilot:
			g_message (_("Merging to pilot..."));
			gpilot_merge_to_pilot (pfd,&stamp,pu,
					      conduit_list,
					      backup_conduit_list,
					      file_conduit_list,
					      context);
			break;
		case GnomePilotConduitSyncTypeMergeFromPilot:
			g_message (_("Merging from pilot..."));
			gpilot_merge_from_pilot (pfd,&stamp,pu,
						conduit_list,
						backup_conduit_list,
						context);
			break;
		case GnomePilotConduitSyncTypeNotSet:
		case GnomePilotConduitSyncTypeCustom:
		default:
			g_message (_("Using conduit settings for sync..."));
			gpilot_sync_default (pfd,&stamp,pu,
					    conduit_list,
					    backup_conduit_list,
					    file_conduit_list,
					    context);
			break;
		}
		g_message (_("Synchronization ended")); 
		gpilot_add_log_entry (pfd,"Synchronization completed");
	} else {
		g_message (_("Synchronization ended early"));
		gpilot_add_log_entry (pfd,"Synchronization terminated");
	}

	write_sync_stamp (pilot,pfd,pu,stamp.sync_PC_Id,time (NULL));
  
	g_free (pilot_name);

	gpilot_unload_conduits (conduit_list);
	gpilot_unload_conduits (backup_conduit_list);
	gpilot_unload_conduits (file_conduit_list);
}

/*
 * This function handles when sync_device (...) encounters a known pilot
 */

static void
gpilot_syncing_known_pilot (GPilotPilot *pilot,
			    struct PilotUser pu,
			    int pfd,
			    GPilotDevice *device,
			    GPilotContext *context)
{
	struct stat buf; 
	int ret;
		
	ret = stat (pilot->sync_options.basedir, &buf); 

	if (ret < 0 || !( S_ISDIR (buf.st_mode) && (buf.st_mode & (S_IRUSR | S_IWUSR |S_IXUSR))) ) {
		
		g_message ("Invalid basedir: %s", pilot->sync_options.basedir);
		gpilot_gui_warning_dialog (_("The base directory %s is invalid.\n"
					     "Please fix it or use gnomecc to choose another directory."),
					   pilot->sync_options.basedir);	
	} else {
		gboolean pwd_ok = TRUE;
		/* If pilot has password, check against the encrypted version
		   on the pilot */
		if (pilot->passwd) {
			char *pwd;
			pwd = (char*)g_malloc (pu.passwordLength);
			strncpy (pwd,pu.password,pu.passwordLength);
			if (g_strcasecmp (pilot->passwd,(char*)crypt (pwd,pilot->passwd))) {
				pwd_ok = FALSE;
				gpilot_gui_warning_dialog (_("Unknown pilot - no pilots matches ID %ld\n"
							     "Use gnomecc to set pilot's ID"),pu.userID);
			}
			g_free (pwd);
		} 
		
		if (pwd_ok) {
			do_sync (pfd,context,&pu,pilot,device);
		}
	}
}

/*
  sync_foreach is the first synchronization entry.

  It first connects to the device on which the signal was detected,
  then it tries to read the user info block from the pilot.

  Hereafter, if there are any events queued for the synchronizing
  cradle, execute them and stop the synchronization (note,
  do_cradle_events returns a bool, if this is FALSE, synchronization
  continues, as some cradle specific events also require a normal sync
  afterwards, eg. the REVIVE call)

  Anyways, if the sync continues, sync_foreach tries to match the
  pilot against the known pilots. If this fails, it should handle it
  intelligently, eg. if the id==0, ask if you want to restore a pilot.

  If the pilot is accepted (dude, there's even a password check!), it
  continues into do_sync, which does all the magic stuff.
*/
   
static gboolean 
sync_device (GPilotDevice *device, GPilotContext *context)
{
	GPilotPilot *pilot;
	int pfd;
	int connect_error;
	struct PilotUser pu;
	struct SysInfo ps;
	
	g_assert (context != NULL);
	g_return_val_if_fail (device != NULL, FALSE);

	/* signal (SIGHUP,SIG_DFL); */
	pfd = pilot_connect (device,&connect_error);

	if (!connect_error) {
		/* connect succeeded, try to read the systeminfo */
		if (dlp_ReadSysInfo (pfd, &ps) < 0) {
			/* no ? drop connection then */
			g_warning (_("An error occured while getting the pilot's system data"));

		/* connect succeeded, try to read the userinfo */
		} else if (dlp_ReadUserInfo (pfd,&pu) < 0) {
			/* no ? drop connection then */
			g_warning (_("An error occured while getting the pilot's user data"));

		} else {
	
			/* If there are cradle specific events, handle them and stop */
			if (do_cradle_events (pfd,context,&pu,device)) {
				g_message (_("Completed events for cradle %s (%s)"),device->name,device->port);
			} else {
				/* No cradle events, validate pilot */
				pilot = gpilot_find_pilot_by_id (pu.userID,context->pilots);

				if (pilot == NULL) {
					/* Pilot is not known */
					gpilot_syncing_unknown_pilot (pu, pfd, device, context);
				} else {
					/* Pilot is known, make connect notifications */
					orbed_notify_connect (pilot->name,pu);				
					gpilot_syncing_known_pilot (pilot, pu, pfd, device, context);
					orbed_notify_disconnect (pilot->name);
				}				
			}
		}
		pilot_disconnect (pfd);
	} else {
		if (connect_error==1) return FALSE; /* remove this device */
		else return TRUE;
	}

	return TRUE;
}

gboolean 
device_in (GIOChannel *io_channel,
	  GIOCondition condition,
	  GPilotContext *context) {
	GPilotDevice *device;
	GList *element;
	gboolean result = TRUE;

	g_assert (context != NULL);
      
	element = g_list_find_custom (context->devices,
				     io_channel,
				     (GCompareFunc)device_equal_by_io);

	if (element==NULL || element->data == NULL) {
		g_warning ("cannot find device for active IO channel");
		return FALSE;
	}
	
	device = element->data; 
	if (context->paused) {
		return FALSE; 
	}	
	g_message (_("Woke on %s"),device->name);
	result = sync_device (device,context);

#ifdef WITH_IRDA
	if (device->type == PILOT_DEVICE_IRDA) {
		g_message ("Restarting irda funk...");
		gpilot_device_deinit (device);
		gpilot_device_init (device);
		monitor_channel (device, context);
		result = FALSE;
	}
#endif /* WITH_IRDA */

	return result;
}

gboolean 
device_err (GIOChannel *io_channel,
	   GIOCondition condition,
	   GPilotContext *context) {
	GPilotDevice *device;
	GList *element;
	char *tmp;

	g_assert (context != NULL);

	switch (condition) {
	case G_IO_IN: tmp = g_strdup_printf ("G_IO_IN"); break;
	case G_IO_OUT : tmp = g_strdup_printf ("G_IO_OUT"); break;
	case G_IO_PRI : tmp = g_strdup_printf ("G_IO_PRI"); break;
	case G_IO_ERR : tmp = g_strdup_printf ("G_IO_ERR"); break;
	case G_IO_HUP : tmp = g_strdup_printf ("G_IO_HUP"); break;
	case G_IO_NVAL: tmp = g_strdup_printf ("G_IO_NVAL"); break;
	default: tmp = g_strdup_printf ("unhandled port error"); break;
	}
	
	element = g_list_find_custom (context->devices,io_channel,(GCompareFunc)device_equal_by_io);

	if (element==NULL) {
		/* We most likely end here if the device has just been removed.
		   Eg. start gpilotd with a monitor on a XCopilot fake serial port,
		   kill xcopilot and watch things blow up as the device fails */
		g_warning ("Device error on some device, caught %s",tmp); 
		g_free (tmp);
		return FALSE;
	}
	
	device = element->data;

	gpilot_gui_warning_dialog ("Device error on %s (%s)\n"
				  "Caught %s",device->name,device->port,tmp); 
	g_warning ("Device error on %s (%s), caught %s",device->name,device->port,tmp);

	remove_device (context,device);
	g_free (tmp);
		
	return FALSE;
}

#ifdef WITH_NETWORK
gboolean 
network_device_in (GIOChannel *io_channel,
	  GIOCondition condition,
	  GPilotContext *context) {

	GPilotDevice *device;
	GList *element;
	gboolean result = TRUE;

	g_assert (context != NULL);
      
	element = g_list_find_custom (context->devices,
				     io_channel,
				     (GCompareFunc)device_equal_by_io);

	if (element==NULL || element->data == NULL) {
		g_warning ("cannot find device for active IO channel");
		return FALSE;
	}
	
	device = element->data; 
	if (context->paused) {
		return FALSE; 
	}	
	g_message (_("Woke on %s"),device->name);
	result = sync_device (device,context);

	return result;
}

gboolean 
network_device_err (GIOChannel *io_channel,
	   GIOCondition condition,
	   GPilotContext *context) {

	GPilotDevice *device;
	GList *element;
	char *tmp;

	g_assert (context != NULL);

	switch (condition) {
		case G_IO_IN: tmp = g_strdup_printf ("G_IO_IN"); break;
		case G_IO_OUT : tmp = g_strdup_printf ("G_IO_OUT"); break;
		case G_IO_PRI : tmp = g_strdup_printf ("G_IO_PRI"); break;
		case G_IO_ERR : tmp = g_strdup_printf ("G_IO_ERR"); break;
		case G_IO_HUP : tmp = g_strdup_printf ("G_IO_HUP"); break;
		case G_IO_NVAL: tmp = g_strdup_printf ("G_IO_NVAL"); break;
		default: tmp = g_strdup_printf ("unhandled port error"); break;
	}
	
	element = g_list_find_custom (context->devices,io_channel,(GCompareFunc)device_equal_by_io);

	if (element==NULL) {
		/* We most likely end here if the device has just been removed.
		   Eg. start gpilotd with a monitor on a XCopilot fake serial port,
		   kill xcopilot and watch things blow up as the device fails */
		g_warning ("Device error on some device, caught %s",tmp); 
		g_free (tmp);
		return FALSE;
	}
	
	device = element->data;

	gpilot_gui_warning_dialog ("Device error on %s (%s)\n"
				  "Caught %s",device->name,device->port,tmp); 
	g_warning ("Device error on %s (%s), caught %s",device->name,device->port,tmp);

	remove_device (context,device);
	g_free (tmp);
		
	return FALSE;
}
#endif /* WITH_NETWORK */

#ifdef WITH_USB_VISOR
#ifdef linux
static const char *product_names[] =
{
	"Handspring Visor",
	"Handspring Treo",
	"Palm Handheld",
	NULL
};

static gboolean product_net[] =
{
	FALSE,
	TRUE,
	TRUE,
	-1
};


static gboolean 
visor_devices_in (GIOChannel *io_channel,
		 GIOCondition condition,
		 GPilotContext *context) {
	GPilotDevice *device;
	GList *l;
	int i;
	gboolean visor_exists = FALSE, visor_net = TRUE;
	char line[256]; /* this is more than enough to fit any line from 
			 * /proc/bus/usb/devices */

	FILE *f;

	g_assert (context != NULL);

	sleep (2);	

	if (context->paused) 
		return FALSE;

	/* Check /proc/bus/usb/devices for a usb device */
	f = fopen ("/proc/bus/usb/devices", "r");

	while (fgets (line, 255, f) != NULL && !visor_exists) {

	/*
	* HACK_ON: identify the Tungsten based on the product id line
	*
	*  (1) before pressing the hotsync button on the USBicraddle
	*  $ cat /proc/bus/usb/devices  > /tmp/before-sync.txt
	*
	*  (2) after pressing the hotsync button on the USB craddle
	*  $ cat /proc/bus/usb/devices  > /tmp/after-sync.txt
	*
	*  (3) difference is
	*  $ diff /tmp/before-sync.txt /tmp/after-sync.txt
	*  ...
	* > P:  Vendor=0830 ProdID=0060 Rev= 1.00
	*  ...
	*********************** This is Tungsten T !
	*/

		if (line[0] == 'P') {
#define TUNGSTEN_STRING "P:  Vendor=0830 ProdID=0060 Rev= 1.00"

			if (!strncmp (line, TUNGSTEN_STRING, strlen(TUNGSTEN_STRING))) {
				g_message("yippie - found my Tungsten T!");
				visor_exists = TRUE;
				visor_net = TRUE;
				goto endloop;
			}
		}

		if (line[0] != 'S')
			continue;
		
		for (i = 0; product_names[i] != NULL; i++) {
			if (!strncmp (line + strlen ("S:  Product="), 
				      product_names[i], 
				      strlen (product_names[i]))) {
				visor_exists = TRUE;
				visor_net = product_net[i];
			}
		}
	}
endloop:	
	fclose (f);

	
	if (visor_exists) {
		l = context->devices;
		while (l) {
			device = l->data;
			if (device->type == PILOT_DEVICE_USB_VISOR) {
				if (!visor_net)
					device->type = PILOT_DEVICE_SERIAL;
				
				/* just try to synch.  Until I can talk to 
				 * the kernel guys this is the best way to 
                                 * go. */
				sync_device (device, context);

				if (!visor_net)
					device->type = PILOT_DEVICE_USB_VISOR;
			}
			l = l->next;
		}
	}

	return TRUE;
}

static gboolean 
visor_devices_err (GIOChannel *io_channel,
	   GIOCondition condition,
	   GPilotContext *context) 
{
	g_assert (context != NULL);

	gpilot_gui_warning_dialog ("Read error on /proc/bus/usb/devices"); 
	g_warning ("Read error on /proc/bus/usb/devices");

	return FALSE;
}

#endif
#endif

void monitor_channel (GPilotDevice *dev,GPilotContext *context) {
	g_assert (context != NULL);
	
	if (dev->type == PILOT_DEVICE_SERIAL
	    || dev->type == PILOT_DEVICE_IRDA) {
		dev->in_handle = g_io_add_watch (dev->io,
						G_IO_IN,
						(GIOFunc)device_in,
						(gpointer)context);
		dev->err_handle = g_io_add_watch (dev->io,
						 G_IO_ERR|G_IO_PRI|G_IO_HUP|G_IO_NVAL,
						 (GIOFunc)device_err,
						 (gpointer)context);
	} else if (dev->type == PILOT_DEVICE_NETWORK) {
#ifdef WITH_NETWORK
		dev->in_handle = g_io_add_watch (dev->io,
						G_IO_IN,
						(GIOFunc)network_device_in,
						(gpointer)context);
		dev->err_handle = g_io_add_watch (dev->io,
						 G_IO_ERR|G_IO_PRI|G_IO_HUP|G_IO_NVAL,
						 (GIOFunc)network_device_err,
						 (gpointer)context);
#else /* WITH_NETWORK */
		g_assert_not_reached ();
#endif /* WITH_NETWORK */
	} if (dev->type == PILOT_DEVICE_USB_VISOR) {
#ifdef WITH_USB_VISOR
#ifdef linux
		/* We want to watch the /proc/bus/usb/devices file once 
		 * per context, and then check all devices each time it is
		 * woken up. */
		if (context->visor_fd == -1) {
			context->visor_fd = open ("/proc/bus/usb/devices",
						  O_RDONLY);
			if (context->visor_fd == -1) {
				g_warning ("Could not open /proc/bus/usb/devices - make sure you have the usbdevfs properly configured and mounted.");
				return;
			}
			context->visor_io = 
				g_io_channel_unix_new (context->visor_fd);
			context->visor_in_handle = 
				g_io_add_watch (context->visor_io,
						G_IO_IN,
						(GIOFunc)visor_devices_in,
						(gpointer)context);
			context->visor_err_handle = 
				g_io_add_watch (context->visor_io,
						G_IO_ERR|G_IO_PRI|G_IO_HUP|G_IO_NVAL,
						(GIOFunc)visor_devices_err,
						(gpointer)context);
		}
#else /* linux*/
		g_assert_not_reached ();
#endif /* linux */
#endif /* WITH_USB_VISOR */
		dev->device_exists = FALSE;
	}

	if (dev->type == PILOT_DEVICE_NETWORK) {
		g_message (_("Watching %s (%s, %s)"), dev->name, dev->ip, dev->host);
	} else {
		g_message (_("Watching %s (%s)"),dev->name,dev->port);
	}
}

static void 
sig_hup_handler (int dummy)
{
	signal (SIGHUP,sig_hup_handler);
	reread_config=TRUE;
}

static void 
sig_term_handler (int dummy) {
	g_message (_("Exiting (caught SIGTERM)..."));
	remove_pid_file ();
	gpilotd_corba_quit ();
	exit (0);
}

static void 
sig_int_handler (int dummy) {
	g_message (_("Exiting (caught SIGINT)..."));
	remove_pid_file ();
	gpilotd_corba_quit ();
	exit (0);
}

/* This deletes the ~/.gpilotd.pid file */
void 
remove_pid_file () {
	gchar *home_directory;
	gchar *pid_file;

	home_directory=g_get_home_dir ();
	if (home_directory) {
		pid_file=(gchar *)g_malloc (strlen (home_directory) + 
					   strlen ("/.gpilotd.pid") + 1);
		strcpy (pid_file,home_directory);
		strcat (pid_file,"/.gpilotd.pid");

		if (access (pid_file,R_OK|W_OK)==0) {
			unlink (pid_file);
		} 
		g_free (pid_file);
	}
}

/*
  The creates a ~/.gilotd.pid, containing the pid
   of the gpilotd process, used by clients to send
   SIGHUPS
*/
static void 
write_pid_file ()
{
	gchar *queue_dir;
	queue_dir = g_strdup_printf ("%s/.gpilotd", g_get_home_dir ());

	if (g_get_home_dir ()) {
		gchar *pid_file;
		int fd;

		pid_file=(gchar *)g_malloc (strlen (g_get_home_dir ()) +
					    strlen ("/.gpilotd.pid") + 1);
		strcpy (pid_file, g_get_home_dir());
		strcat (pid_file,"/.gpilotd.pid");
		if ((fd=open (pid_file,O_RDWR | O_TRUNC | O_CREAT, 0644)) >= 0) {
			gchar pid[50]; /* If the pid is > 50 digits we have problems :) */
			if (sizeof (pid_t) == sizeof (int)) {
				g_snprintf (pid,50,"%d\n",getpid ());
			} else { /* Assume pid_t is sizeof long */
				g_snprintf (pid,50,"%ld\n",(unsigned long)getpid ());
			}
			write (fd,pid,strlen (pid));
			close (fd);
		} else {
			g_warning (_("Unable to open or create "
				    "file %s with read/write privs"),pid_file);
		}

		g_free (pid_file);
	} else {
		g_warning (_("Unable to find home directory for uid %d"),geteuid ());
	}
	if (g_file_test (queue_dir, G_FILE_TEST_ISDIR)==FALSE) {
		if (mkdir (queue_dir, 0700)) {
			g_warning (_("Unable to create file installation queue directory"));
		}
	}
	g_free (queue_dir);
}

/*  
    The main loop. Sets up monitors for the devices and calls
    g_main_iteration to wait for a sync. The monitor handler handles
    the sync, so look in device_in for the call to the actual sync.

    If reread_config gets set to TRUE (by a SIGHUP signal, free
    all devices and context and reload it 
*/

static void 
wait_for_sync_and_sync (GPilotContext *context) {
	signal (SIGTERM,sig_term_handler);
	signal (SIGINT,sig_int_handler);
	signal (SIGHUP,sig_hup_handler);

	g_list_foreach (context->devices, (GFunc)monitor_channel, context);

	while (1) {
		if (reread_config) {
			g_message (_("Shutting down devices"));
			gpilot_context_free (context);
			g_message (_("Rereading configuration..."));
			gpilot_context_init_user (context);
			reread_config=FALSE;
			g_list_foreach (context->devices, (GFunc)monitor_channel, context);
		}
		/* Enter the gtk main loop */
		g_main_iteration (TRUE);
	}
}

/* This function display which pilot-link version was used
   and which features were enabled at compiletime */
static void
dump_build_info ()
{
	GString *str = g_string_new (NULL);
	g_message ("compiled for pilot-link version %s",
		   GP_PILOT_LINK_VERSION);

	str = g_string_append (str, "compiled with ");
#ifdef WITH_VFS
	str = g_string_append (str, "[VFS] ");
#endif
#ifdef WITH_USB_VISOR
	str = g_string_append (str, "[USB] ");
#endif
#ifdef WITH_IRDA
	str = g_string_append (str, "[IrDA] ");
#endif
#ifdef WITH_NETWORK
	str = g_string_append (str, "[Network] ");
#endif
	g_message (str->str);
	g_string_free (str, TRUE);
}

int 
main (int argc, char *argv[])
{
	GPilotContext *context;

	bindtextdomain (PACKAGE, GNOMELOCALEDIR);
	textdomain (PACKAGE);

	/*g_log_set_always_fatal (G_LOG_LEVEL_ERROR |
				G_LOG_LEVEL_CRITICAL |
				G_LOG_LEVEL_WARNING);*/

	/* Intro */
	g_message ("%s %s starting...",PACKAGE,VERSION);
	dump_build_info ();


	/* Setup the correct gpilotd.pid file */
	remove_pid_file ();
	write_pid_file ();

	/* Init corba and context, this call also loads the config into context */
	gtk_type_init ();
	gpilotd_corba_init (&argc,argv,&context);

	reread_config=FALSE;	

	/* Begin... */
	wait_for_sync_and_sync (context);

	/* It is unlikely that we will end here */
	remove_pid_file ();
	gpilotd_corba_quit ();
	
	return 0;
}









[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]