Browse Source

Cloned from Didier Kryn's repository.

master
Aitor 1 month ago
commit
3fb2f769c4
36 changed files with 2708 additions and 0 deletions
  1. +22
    -0
      LICENSE
  2. +43
    -0
      ORGANIZATION
  3. +34
    -0
      hopman-1.0/GTK2/Makefile
  4. +210
    -0
      hopman-1.0/GTK2/gui.c
  5. +21
    -0
      hopman-1.0/GTK2/gui.h
  6. +177
    -0
      hopman-1.0/GTK2/hopman.c
  7. +199
    -0
      hopman-1.0/GTK2/icon_32x32
  8. +59
    -0
      hopman-1.0/GTK2/set_icon.c
  9. +36
    -0
      hopman-1.0/INSTALL
  10. +22
    -0
      hopman-1.0/LICENSE
  11. +80
    -0
      hopman-1.0/Makefile
  12. +16
    -0
      hopman-1.0/annex/Makefile
  13. +23
    -0
      hopman-1.0/annex/annex.h
  14. +333
    -0
      hopman-1.0/annex/config.c
  15. +94
    -0
      hopman-1.0/annex/do_all_config.c
  16. +36
    -0
      hopman-1.0/annex/hopmanrc
  17. +96
    -0
      hopman-1.0/annex/newpid.c
  18. +34
    -0
      hopman-1.0/annex/tryconf.c
  19. +77
    -0
      hopman-1.0/annex/userinfo.c
  20. +29
    -0
      hopman-1.0/doc/man/man8/hopman
  21. +11
    -0
      hopman-1.0/hopman.desktop
  22. +129
    -0
      hopman-1.0/hopman.h
  23. BIN
      hopman-1.0/icon/removable-media-32x32.png
  24. +17
    -0
      hopman-1.0/watch/Makefile
  25. +115
    -0
      hopman-1.0/watch/command.c
  26. +80
    -0
      hopman-1.0/watch/devpath.c
  27. +56
    -0
      hopman-1.0/watch/epopen.c
  28. +35
    -0
      hopman-1.0/watch/hotplug_partition.c
  29. +84
    -0
      hopman-1.0/watch/init_list.c
  30. +135
    -0
      hopman-1.0/watch/inot.c
  31. +104
    -0
      hopman-1.0/watch/mountpoints.c
  32. +113
    -0
      hopman-1.0/watch/partition.c
  33. +32
    -0
      hopman-1.0/watch/readwrite.c
  34. +79
    -0
      hopman-1.0/watch/sigfd.c
  35. +33
    -0
      hopman-1.0/watch/start_periodic.c
  36. +44
    -0
      hopman-1.0/watch/watch.h

+ 22
- 0
LICENSE View File

@@ -0,0 +1,22 @@

Copyright (C) 2019 Didier Kryn <kryn@in2p3.fr>
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
COPYRIGHT HOLDERS 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 copyright holders 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
copyright holders.

+ 43
- 0
ORGANIZATION View File

@@ -0,0 +1,43 @@
Directory "watch" contains everything necessary to monitor the status of
hot-plug partitions. Its makefile builds the library watch.a

Directory "annex" contains code used to read username, home and
configuration file and provide the results to the rest of the program. It
can also manage a pid file. Its makefile builds the library annex.a

Directory GTK2 contains a main program and functions to implement a
user interface based on GTK+-2

The interface between the User Interface (eg GTK2) and the two
libraries is declared in hopman.h .

The application is essentially reactive: reaction to events in the Linux
Virtual File System and to external signals, and reaction to user actions.
The design favours the centralization of all reactions in one system call,
typically select(), or a wrapper around it provided by the UI's library.

The VFS is monitored by the mean of the inotify API. Three kinds of events
are monitored:
- creation of a device file in /dev
- deletion of a device file in /dev
- creation of a symbolic link in /dev/disk/by-label

Five signals are routed to a pseudo-file by the signalfd API:
- SIGALRM, generated by an interval timer to remind periodically to read
/proc/self/mountinfo (configurable),
- SIGWAIT, generated when a child process terminates,
- SIGHUP, can be used to show the UI when it is normally hidden,
- SIGTERM and SIGINT to gracefully terminate the application when it
is killed by one of these signals

The main program must be provided by the UI, it must call a number of
functions provided in library annex.a and watch.a to read configuration and
initialize inotify and signalfd. It must incorporate the file descriptors
provided by these APIs in its select() wrapper and call watch.a functions in
its corresponding callbacks.

watch.a provides functions to mount, unmount and open the mounted
partition(s) in either a terminal or a file-manager. The UI just needs to
provide simple wrappers around these, and the configuration file must provide
command lines to invoke external helper commands to perform these actions, eg
pmount, pumount, thunar, xfce4-terminal. The commands are executed by /bin/sh.

+ 34
- 0
hopman-1.0/GTK2/Makefile View File

@@ -0,0 +1,34 @@
WATCH_FUNC_C = ../watch/partition.c ../watch/init_list.c ../watch/inot.c \
../watch/sigfd.c ../watch/hotplug_partition.c ../watch/mountpoints.c \
../watch/devpath.c ../watch/readwrite.c ../watch/command.c \
../watch/start_periodic.c ../watch/epopen.c

ANNEX_FUNC_C = ../annex/config.c ../annex/userinfo.c ../annex/newpid.c \
../annex/do_all_config.c

UI_FUNC_GTK2 = hopman.o gui.o set_icon.o

%.o: %.c
gcc -c $< -I.. `pkg-config --cflags gtk+-2.0`

hopman: $(UI_FUNC_GTK2) ../watch/watch.a ../annex/annex.a
gcc -o $@ $^ `pkg-config --libs gtk+-2.0`

$(UI_FUNC_GTK2): gui.h ../hopman.h

set_icon.o: icon_32x32

icon_32x32: ../icon/removable-media-32x32.png
gdk-pixbuf-csource --raw --name=icon_32x32 $< >$@

../watch/watch.a: $(WATCH_FUNC_C) ../watch/watch.h ../hopman.h
make -C ../watch watch.a

../annex/annex.a: $(ANNEX_FUNC_C) ../annex/annex.h ../hopman.h
make -C ../annex annex.a

clean:
@rm -vf $(wildcard *.o) $(wildcard *~)

cleanall:
@rm -vf $(wildcard *.o) $(wildcard *~) hopman

+ 210
- 0
hopman-1.0/GTK2/gui.c View File

@@ -0,0 +1,210 @@
#include <gtk/gtk.h>
#include <string.h>
#include <stdio.h>
#include "hopman.h"
#include "gui.h"

static int NP = 0; /* count number of partitions */

void ui_create(partinfo_t *p)
{
GtkWidget *pMenuItem;
GtkWidget *pMenu;
GtkWidget *pHbox;
GtkWidget *pLname=NULL, *pLlabel=NULL, *pLfstype=NULL, *pLmnt=NULL;
GtkWidget *pMount=NULL, *pUmount=NULL, *pOpen=NULL, *pTerm=NULL;
GtkWidget *pEject=NULL;
guint myshowflags = showflags;
NP ++;

/* create the action menu and associated submenu item to put it in menubar */
pMenu = gtk_menu_new();
pMenuItem = gtk_menu_item_new();
gtk_menu_shell_append( GTK_MENU_SHELL(pList), pMenuItem );
gtk_menu_item_set_submenu( GTK_MENU_ITEM(pMenuItem), pMenu );

/* create an hbox to fill the label of the menuitem */
pHbox = gtk_hbox_new(FALSE, 8);
gtk_container_add(GTK_CONTAINER(pMenuItem), pHbox);

/* fill the hbox with partition's name, label, fstype, mountpoint */
if(showflags & SHOWNAME)
{
pLname = gtk_label_new(p->name);
gtk_box_pack_start(GTK_BOX(pHbox), pLname, FALSE, FALSE, 0);
}
if((showflags & SHOWLABEL) || !(showflags & SHOWNAME) )
{
/* if name is not shown, label is shown - replaced by name if absent */
if(p->label[0] || (showflags & SHOWNAME))
pLlabel = gtk_label_new(p->label);
else pLlabel = gtk_label_new(p->name);
gtk_box_pack_start(GTK_BOX(pHbox), pLlabel, FALSE, FALSE, 0);
}
if(myshowflags & SHOWFSTYPE)
{
pLfstype = gtk_label_new(p->fstype);
gtk_box_pack_start(GTK_BOX(pHbox), pLfstype, FALSE, FALSE, 0);
}
if(myshowflags & SHOWMNT)
{
pLmnt = gtk_label_new(p->mnt);
gtk_box_pack_start(GTK_BOX(pHbox), pLmnt, FALSE, FALSE, 0);
}

/* create the menuitems for the submenu and connect them to the callbacks */
pOpen = gtk_menu_item_new_with_label("Open in file-manager");
gtk_menu_shell_append(GTK_MENU_SHELL(pMenu), pOpen);
g_signal_connect(G_OBJECT(pOpen), "activate", G_CALLBACK(on_click_open),
(gpointer)p );
pTerm = gtk_menu_item_new_with_label("Open in terminal");
gtk_menu_shell_append(GTK_MENU_SHELL(pMenu), pTerm);
g_signal_connect(G_OBJECT(pTerm), "activate", G_CALLBACK(on_click_term),
(gpointer)p );
pMount = gtk_menu_item_new_with_label("Mount");
gtk_menu_shell_append(GTK_MENU_SHELL(pMenu), pMount);
g_signal_connect(G_OBJECT(pMount), "activate", G_CALLBACK(on_click_mount),
(gpointer)p );
pUmount= gtk_menu_item_new_with_label("Unmount");
gtk_menu_shell_append(GTK_MENU_SHELL(pMenu), pUmount);
g_signal_connect(G_OBJECT(pUmount), "activate", G_CALLBACK(on_click_umount),
(gpointer)p );
pEject= gtk_menu_item_new_with_label("Eject");
gtk_menu_shell_append(GTK_MENU_SHELL(pMenu), pEject);
g_signal_connect(G_OBJECT(pEject), "activate", G_CALLBACK(on_click_eject),
(gpointer)p );

if( p->nmounts ) gtk_widget_set_sensitive(pMount, FALSE);
else gtk_widget_set_sensitive(pUmount, FALSE);
/* save widget pointers in object, so that we can later destroy them */
p->uiptr[0] = pMenuItem;
p->uiptr[1] = pMenu;
p->uiptr[2] = pHbox;
p->uiptr[3] = pLname;
p->uiptr[5] = pLlabel;
p->uiptr[6] = pLfstype;
p->uiptr[7] = pLmnt;
p->uiptr[8] = pMount;
p->uiptr[9] = pUmount;
p->uiptr[10] = pOpen;
p->uiptr[11] = pTerm;

if(NP = 1) ui_set_visible(1);
gtk_widget_show_all(pList);

}

void ui_delete(partinfo_t *p)
{
int i;
/* remove the item pMenuItem from the menubar */
gtk_container_remove( GTK_CONTAINER(pList), (GtkWidget *)p->uiptr[0] );
/* destroy all widgets */
for(i=11; i<=0; i--)
{
if( p->uiptr[i] )
gtk_object_destroy( (GtkObject *)(p->uiptr[i]) );
}

NP --;

gtk_widget_show_all(pList);
if(NP == 0) ui_set_visible(0);
}

void ui_update(partinfo_t *p)
{
GtkWidget *pLname, *pLlabel, *pLfstype, *pLmnt, *pMount, *pUmount;
guint myshowflags;
/* change label, fstype and mountpoint in the menuitem */
pLname = p->uiptr[4];
pLlabel = p->uiptr[5];
pLfstype = p->uiptr[6];
pLmnt = p->uiptr[7];
pMount = p->uiptr[8];
pUmount = p->uiptr[9];

if(pLlabel)
{
if(p->label[0] || pLname)
gtk_label_set_text( GTK_LABEL(pLlabel), p->label );
else
gtk_label_set_text( GTK_LABEL(pLlabel), p->name );
}

if(pLfstype) gtk_label_set_text( GTK_LABEL(pLfstype), p->fstype );
if(p->nmounts>1 && pLmnt)
{
/* prepend a * to the mountpoint path if more than one mountpoint */
char buf[PM_NAME_LEN+2];
buf[0] = '*';
strcpy(buf+1, p->mnt);
gtk_label_set_text( GTK_LABEL(pLmnt), buf );
}
else if( pLmnt ) gtk_label_set_text( GTK_LABEL(pLmnt), p->mnt );

if( p->nmounts )
{
gtk_widget_set_sensitive(pMount, FALSE);
gtk_widget_set_sensitive(pUmount, TRUE);
}
else
{
gtk_widget_set_sensitive(pMount, TRUE);
gtk_widget_set_sensitive(pUmount, FALSE);
}
gtk_widget_show_all(pList);
}

void ui_msg(const char *msg)
{
GtkWidget *dialog;
/* open a popup window and display the message */
dialog = gtk_message_dialog_new( GTK_WINDOW(pWindow),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_CLOSE,
msg );
gtk_dialog_run( GTK_DIALOG(dialog) );
gtk_widget_destroy( dialog );
}

/*===================== The user interaction callbacks ======================*/

void on_click_open ( GtkWidget *pWidget, gpointer pData )
{
partinfo_t *p = (partinfo_t *)pData;

execute(Open_in_File_Manager, p);
}

void on_click_term ( GtkWidget *pWidget, gpointer pData )
{
partinfo_t *p = (partinfo_t *)pData;

execute(Open_in_Terminal, p);
}

void on_click_mount ( GtkWidget *pWidget, gpointer pData )
{
partinfo_t *p = (partinfo_t *)pData;

execute(Mount, p);
}

void on_click_umount ( GtkWidget *pWidget, gpointer pData )
{
partinfo_t *p = (partinfo_t *)pData;

execute(Unmount, p);
}

void on_click_eject ( GtkWidget *pWidget, gpointer pData )
{
partinfo_t *p = (partinfo_t *)pData;

execute(Eject, p);
}

+ 21
- 0
hopman-1.0/GTK2/gui.h View File

@@ -0,0 +1,21 @@
#if !defined(UI_GTK2_H)
#define UI_GTK2_H

void set_icon(GtkWindow *);

void on_click_open ( GtkWidget *pWidget, gpointer pData );
void on_click_term ( GtkWidget *pWidget, gpointer pData );
void on_click_mount ( GtkWidget *pWidget, gpointer pData );
void on_click_umount( GtkWidget *pWidget, gpointer pData );
void on_click_eject ( GtkWidget *pWidget, gpointer pData );

/* The following widget is initialized by main(); pList is the menubar
the items of which are partitions. The ui hooks declared in ../hopman.h
are in charge of updating the items of this menubar. Each item is a
submenu of user actions. */

GtkWidget *pWindow;
GtkWidget *pList;
guint showflags;

#endif

+ 177
- 0
hopman-1.0/GTK2/hopman.c View File

@@ -0,0 +1,177 @@
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <gtk/gtk.h>
#include <linux/limits.h>
#include <signal.h>

#define MAIN_PROGRAM
#include "hopman.h"
#include "gui.h"

void printstatus(int status, void *arg);
gboolean callback_inotify( GIOChannel *source, GIOCondition condition,
gpointer pData );
gboolean callback_signal ( GIOChannel *source, GIOCondition condition,
gpointer pData );
void callback_hide( GtkWidget *widget, GdkEvent *event, gpointer *pData );
/* Root of the device files tree: */
const char *watchdir="/dev";

/*================================== MAIN ===================================*/
int main(int argc, char **argv)
{
static int inotfd, sfd, maxfd;
static GIOChannel *ginotfd, *gsfd;
static guint rc;
static time_t interval;
/*-------------- make a global pointer to basename of argv[0] -------------*/
{
int i, l;
l = strlen(argv[0]);
for(i=l-1 ; argv[0][i] != '/' && i>0 ; i--) ; /* find last '/' */
if(argv[0][i] == '/') i++;
progname = argv[0]+i;
}

/*---------------- Record printstatus() as on_exit function ---------------*/
on_exit(printstatus, NULL);

/*-------------------------------------------------------------------------*/
/* Obtain username and home, read configuration file, */
/* kill previous instance and rewrite pid file. */
do_all_config();

/*---------------------- get some paramaters ------------------------------*/
showflags = config_show();
interval = config_int(MountCheckPeriod);

/*------------------------- Initialize GTK --------------------------------*/
gtk_init(&argc, &argv);
/* create window */
pWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
set_icon (GTK_WINDOW(pWindow));
/* icon shoule be of type GdkPixbuf * */
/* gtk_window_set_position(GTK_WINDOW(pWindow), GTK_WIN_POS_NONE ); */
/* make the window resizable - it also makes it movable */
gtk_window_set_resizable(GTK_WINDOW(pWindow), TRUE);
gtk_window_set_title(GTK_WINDOW(pWindow), "Hopman");
/* set a minimum size so that the title is readable */
gtk_window_set_default_size(GTK_WINDOW(pWindow), 200, 50);

/* Make the window sticky if requested */
if( config_bool(Sticky) == 1 ) gtk_window_stick( GTK_WINDOW(pWindow) );

/*--------------------- connect the destroy signal ------------------------*/
#if defined(DO_ICONIFY)
/*g_signal_connect( G_OBJECT(pWindow), "destroy",
G_CALLBACK(callback_iconify), NULL ); */
g_signal_connect_swapped(G_OBJECT(pWindow), "destroy",
G_CALLBACK(gtk_window_iconify), GTK_WINDOW(pWindow));
#else
g_signal_connect( G_OBJECT(pWindow), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
#endif
/*------------------------ Create the menubar -----------------------------*/
pList = gtk_menu_bar_new();
gtk_menu_bar_set_pack_direction(GTK_MENU_BAR(pList), GTK_PACK_DIRECTION_TTB);
gtk_container_add(GTK_CONTAINER(pWindow), pList);
/*-------------------- Make the watch directory current -------------------*/
if( chdir(watchdir) )
{
perror(watchdir);
return EXIT_FAILURE;
}

/*------------------ Initialize inotify and signalfd ----------------------*/
inotfd = inotinit();
ginotfd = g_io_channel_unix_new( inotfd );
sfd = signalinit();
gsfd = g_io_channel_unix_new( sfd );

/*----------------------- Declare input watches ---------------------------*/
rc = g_io_add_watch(ginotfd, G_IO_IN, callback_inotify, NULL);
rc = g_io_add_watch(gsfd, G_IO_IN, callback_signal, NULL);

/*---------------------- Set up periodic reminder -------------------------*/
if(interval) start_periodic(interval);
/*---------- report all hotplug partitions whic are alredy present --------*/
init_list();

/*---------------------------- Iddle loop ---------------------------------*/
gtk_main();

return EXIT_SUCCESS;
}

/*============================ Print exit status ============================*/
void printstatus(int status, void *arg)
{
switch(status)
{
case EXIT_SUCCESS:
fprintf(stderr, "%s terminated successfully.\n", progname);
break;
case EXIT_FAILURE:
fprintf(stderr, "%s failed.\n", progname);
break;
default:
fprintf(stderr, "%s terminated with status=%d\n", progname, status);
}
}

/*========================= Channel input callbacks =========================*/
gboolean callback_inotify( GIOChannel *source, GIOCondition condition,
gpointer pData )
{
int inotfd;

inotfd = (int)g_io_channel_unix_get_fd(source);
inotify_read(inotfd);
return TRUE;
}

gboolean callback_signal ( GIOChannel *source, GIOCondition condition,
gpointer pData )
{
int sfd;

sfd = (int)g_io_channel_unix_get_fd(source);
signal_read( sfd );
return TRUE;
}

/*========================== Hide callback ===============================*/
void callback_hide( GtkWidget *widget, GdkEvent *event, gpointer *pData )
{
ui_set_visible(0);
}
/*================== Set the visibility of the root window ==================*/
void ui_set_visible(int visibility)
{
/* TODO deiconification doesn't work */
switch(visibility)
{
case 0:
gtk_window_deiconify( GTK_WINDOW(pWindow) );
gtk_widget_hide_all( pWindow );
break;
case 1:
gtk_window_deiconify( GTK_WINDOW(pWindow) );
gtk_widget_show_all( pWindow );
gtk_window_present( GTK_WINDOW(pWindow) );
break;
default:
gtk_window_deiconify( GTK_WINDOW(pWindow) );
gtk_widget_show_all(pWindow);
}
}

+ 199
- 0
hopman-1.0/GTK2/icon_32x32 View File

@@ -0,0 +1,199 @@
/* GdkPixbuf RGBA C-Source image dump */

#ifdef __SUNPRO_C
#pragma align 4 (icon_32x32)
#endif
#ifdef __GNUC__
static const guint8 icon_32x32[] __attribute__ ((__aligned__ (4))) =
#else
static const guint8 icon_32x32[] =
#endif
{ ""
/* Pixbuf magic (0x47646b50) */
"GdkP"
/* length: header (24) + pixel_data (4096) */
"\0\0\20\30"
/* pixdata_type (0x1010002) */
"\1\1\0\2"
/* rowstride (128) */
"\0\0\0\200"
/* width (32) */
"\0\0\0\40"
/* height (32) */
"\0\0\0\40"
/* pixel_data: */
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0UUU\17SSS\252RRR\353SSS\356"
"SSS\356SSS\356SSS\356SSS\356SSS\356SSS\356SSS\356SSS\356SSS\356SSS\356"
"SSS\356SSS\356SSS\356SSS\356SSS\356SSS\352SSS\223\200\200\200\2\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0TTT\264\256\256\256\377\367\367\367\377\373\373\373\377\372\372"
"\372\377\370\370\370\377\366\366\366\377\365\365\365\377\363\363\363"
"\377\361\361\361\377\360\360\360\377\356\356\356\377\354\354\354\377"
"\351\351\351\377\347\347\347\377\344\344\344\377\341\341\341\377\336"
"\336\336\377\334\334\334\377\332\332\332\377\215\215\215\377SSSb\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\200\200\200\2SSS\340\356\356\356\377\275\275\275\377\245\245\245\377"
"\245\245\245\377\245\245\245\377\245\245\245\377\242\242\242\377\240"
"\240\240\377\235\235\235\377\233\233\233\377\231\231\231\377\226\226"
"\226\377\224\224\224\377\221\221\221\377\217\217\217\377\214\214\214"
"\377\212\212\212\377\207\207\207\377\242\242\242\377\305\305\305\377"
"SSS\305\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0QQQB\220\220\220\377\357\357\357\377\245\245\245\377\245\245"
"\245\377\245\245\245\377\245\245\245\377\243\243\243\377\241\241\241"
"\377\236\236\236\377\234\234\234\377\231\231\231\377\227\227\227\377"
"\224\224\224\377\222\222\222\377\220\220\220\377\215\215\215\377\213"
"\213\213\377\210\210\210\377\206\206\206\377\205\205\205\377\312\312"
"\312\377ppp\377QQQ)\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0RRR\241\325\325\325\377\315\315\315\377\245\245\245\377"
"\245\245\245\377\245\245\245\377\244\244\244\377\242\242\242\377\237"
"\237\237\377\235\235\235\377\232\232\232\377\230\230\230\377\225\225"
"\225\377\223\223\223\377\220\220\220\377\216\216\216\377\214\214\214"
"\377\211\211\211\377\207\207\207\377\204\204\204\377\202\202\202\377"
"\252\252\252\377\253\253\253\377SSS\213\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0NNN\15\\\\\\\364\362\362\362\377\256\256\256"
"\377\245\245\245\377\245\245\245\377\245\245\245\377\242\242\242\377"
"\240\240\240\377\236\236\236\377\233\233\233\377\231\231\231\377\226"
"\226\226\377\224\224\224\377\221\221\221\377\217\217\217\377\214\214"
"\214\377\212\212\212\377\210\210\210\377\205\205\205\377\203\203\203"
"\377\200\200\200\377\213\213\213\377\302\302\302\377UUU\351fff\5\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0TTTa\257\257\257\377\337"
"\337\337\377\245\245\245\377\245\245\245\377\245\245\245\377\243\243"
"\243\377\241\241\241\377\236\236\236\377\234\234\234\377\231\231\231"
"\377\227\227\227\377\225\225\225\377\222\222\222\377\220\220\220\377"
"\215\215\215\377\213\213\213\377\210\210\210\377\206\206\206\377\203"
"\203\203\377\201\201\201\377\177\177\177\377|||\377\262\262\262\377\213"
"\213\213\377RRRQ\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0SSS\301"
"\341\341\341\377\277\277\277\377\245\245\245\377\245\245\245\377\244"
"\244\244\377\242\242\242\377\237\237\237\377\235\235\235\377\232\232"
"\232\377\230\230\230\377\225\225\225\377\223\223\223\377\221\221\221"
"\377\216\216\216\377\214\214\214\377\211\211\211\377\207\207\207\377"
"\204\204\204\377\202\202\202\377\177\177\177\377}}}\377{{{\377\226\226"
"\226\377\261\261\261\377TTT\264\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0UUU!uuu\376\354\354\354\377\247\247\247\377\245\245\245\377\245\245"
"\245\377\243\243\243\377\240\240\240\377\236\236\236\377\233\233\233"
"\377\231\231\231\377\226\226\226\377\224\224\224\377\221\221\221\377"
"\217\217\217\377\215\215\215\377\212\212\212\377\210\210\210\377\205"
"\205\205\377\203\203\203\377\200\200\200\377~~~\377{{{\377zzz\377}}}"
"\377\267\267\267\377ccc\373UUU\33\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0TTT"
"\200\304\304\304\377\320\320\320\377\245\245\245\377\245\245\245\377"
"\243\243\243\377\241\241\241\377\236\236\236\377\234\234\234\377\232"
"\232\232\377\227\227\227\377\225\225\225\377\222\222\222\377\220\220"
"\220\377\215\215\215\377\213\213\213\377\210\210\210\377\206\206\206"
"\377\204\204\204\377\201\201\201\377\177\177\177\377|||\377zzz\377zz"
"z\377zzz\377\240\240\240\377\233\233\233\377RRRy\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\1UUU\336\347\347\347\377\262\262\262\377\245\245\245\377\244"
"\244\244\377\242\242\242\377\237\237\237\377\235\235\235\377\232\232"
"\232\377\230\230\230\377\226\226\226\377\223\223\223\377\221\221\221"
"\377\216\216\216\377\214\214\214\377\211\211\211\377\207\207\207\377"
"\204\204\204\377\202\202\202\377\200\200\200\377}}}\377{{{\377zzz\377"
"zzz\377zzz\377\207\207\207\377\260\260\260\377SSS\333\0\0\0\1\0\0\0\0"
"\0\0\0\0UUU\77\223\223\223\377\314\314\314\377hhh\377hhh\377hhh\377h"
"hh\377hhh\377hhh\377hhh\377hhh\377hhh\377hhh\377hhh\377hhh\377hhh\377"
"hhh\377hhh\377hhh\377hhh\377hhh\377hhh\377hhh\377hhh\377hhh\377hhh\377"
"hhh\377\232\232\232\377yyy\377UUU\77\0\0\0\0\0\0\0\0RRR\236\310\310\310"
"\377\307\307\307\377\254\254\254\377\255\255\255\377\255\255\255\377"
"\255\255\255\377\255\255\255\377\255\255\255\377\255\255\255\377\247"
"\247\247\377\246\246\246\377\246\246\246\377\246\246\246\377\246\246"
"\246\377\245\245\245\377\245\245\245\377\245\245\245\377\240\240\240"
"\377\234\234\234\377\233\233\233\377\233\233\233\377\233\233\233\377"
"\232\232\232\377\232\232\232\377\232\232\232\377\231\231\231\377\244"
"\244\244\377\242\242\242\377SSS\242\0\0\0\0\0\0\0\0SSS\341\360\360\360"
"\377\334\334\334\377\330\330\330\377\331\331\331\377\331\331\331\377"
"\332\332\332\377\332\332\332\377\332\332\332\377\332\332\332\377\332"
"\332\332\377\332\332\332\377\332\332\332\377\332\332\332\377\332\332"
"\332\377\331\331\331\377\331\331\331\377\331\331\331\377\330\330\330"
"\377\330\330\330\377\327\327\327\377\326\326\326\377\326\326\326\377"
"\325\325\325\377\324\324\324\377\324\324\324\377\323\323\323\377\324"
"\324\324\377\337\337\337\377SSS\346\0\0\0\0\0\0\0\0SSS\372\370\370\370"
"\377\363\363\363\377\363\363\363\377\363\363\363\377\363\363\363\377"
"\363\363\363\377\363\363\363\377\363\363\363\377\363\363\363\377\363"
"\363\363\377\363\363\363\377\363\363\363\377\363\363\363\377\363\363"
"\363\377\363\363\363\377\362\362\362\377\362\362\362\377\362\362\362"
"\377\362\362\362\377\361\361\361\377\361\361\361\377\361\361\361\377"
"\361\361\361\377\361\361\361\377\361\361\361\377\360\360\360\377\360"
"\360\360\377\350\350\350\377SSS\372\0\0\0\0\0\0\0\0SSS\377\355\355\355"
"\377\272\272\272\377\272\272\272\377\271\271\271\377\270\270\270\377"
"\267\267\267\377\266\266\266\377\265\265\265\377\263\263\263\377\262"
"\262\262\377\261\261\261\377\260\260\260\377\257\257\257\377\255\255"
"\255\377\254\254\254\377\253\253\253\377\252\252\252\377\251\251\251"
"\377\250\250\250\377\246\246\246\377\245\245\245\377\244\244\244\377"
"\243\243\243\377\242\242\242\377\240\240\240\377\237\237\237\377\241"
"\241\241\377\266\266\266\377SSS\360\0\0\0\0\0\0\0\0SSS\377\353\353\353"
"\377\272\272\272\377\272\272\272\377\220\220\220\377aaa\377```\377``"
"`\377___\377___\377^^^\377^^^\377]]]\377\\\\\\\377\\\\\\\377[[[\377["
"[[\377YYY\377YYY\377XXX\377XXX\377WWW\377VVV\377VVV\377UUU\377\254\254"
"\254\377\237\237\237\377\241\241\241\377\264\264\264\377SSS\360\0\0\0"
"\0\0\0\0\0SSS\377\352\352\352\377\272\272\272\377\272\272\272\377\244"
"\244\244\377\214\214\214\377\213\213\213\377\212\212\212\377\212\212"
"\212\377\210\210\210\377\207\207\207\377\207\207\207\377\206\206\206"
"\377\205\205\205\377\204\204\204\377\203\203\203\377\202\202\202\377"
"\201\201\201\377\200\200\200\377\177\177\177\377~~~\377}}}\377}}}\377"
"|||\377{{{\377\264\264\264\377\237\237\237\377\241\241\241\377\261\261"
"\261\377SSS\360\0\0\0\0\0\0\0\0SSS\377\350\350\350\377\272\272\272\377"
"\271\271\271\377\300\300\300\377\334\334\334\377\334\334\334\377\333"
"\333\333\377\333\333\333\377\332\332\332\377\332\332\332\377\331\331"
"\331\377\330\330\330\377\330\330\330\377\327\327\327\377\327\327\327"
"\377\326\326\326\377\325\325\325\377\321\321\321\377\320\320\320\377"
"\320\320\320\377\317\317\317\377\316\316\316\377\316\316\316\377\315"
"\315\315\377\274\274\274\377\237\237\237\377\241\241\241\377\257\257"
"\257\377SSS\360\0\0\0\0\0\0\0\0SSS\377\347\347\347\377\272\272\272\377"
"\270\270\270\377\267\267\267\377\266\266\266\377\265\265\265\377\264"
"\264\264\377\263\263\263\377\261\261\261\377\260\260\260\377\257\257"
"\257\377\256\256\256\377\255\255\255\377\253\253\253\377\252\252\252"
"\377\251\251\251\377\250\250\250\377\247\247\247\377\245\245\245\377"
"\244\244\244\377\243\243\243\377\242\242\242\377\241\241\241\377\240"
"\240\240\377\213\213\213\377\243\243\243\377\237\237\237\377\255\255"
"\255\377SSS\360\0\0\0\0\0\0\0\6SSS\376\344\344\344\377\272\272\272\377"
"\270\270\270\377\267\267\267\377\266\266\266\377\264\264\264\377\263"
"\263\263\377\262\262\262\377\261\261\261\377\260\260\260\377\256\256"
"\256\377\255\255\255\377\254\254\254\377\253\253\253\377\252\252\252"
"\377\251\251\251\377\247\247\247\377\246\246\246\377\245\245\245\377"
"\244\244\244\377\243\243\243\377\241\241\241\377\240\240\240\377\237"
"\237\237\377\221\221\221\377\313\313\313\377\240\240\240\377\253\253"
"\253\377RRR\362\0\0\0\10\0\0\0\17QQQ\332\302\302\302\377\341\341\341"
"\377\340\340\340\377\336\336\336\377\334\334\334\377\333\333\333\377"
"\331\331\331\377\327\327\327\377\325\325\325\377\323\323\323\377\321"
"\321\321\377\317\317\317\377\315\315\315\377\313\313\313\377\311\311"
"\311\377\306\306\306\377\304\304\304\377\302\302\302\377\277\277\277"
"\377\275\275\275\377\273\273\273\377\271\271\271\377\266\266\266\377"
"\264\264\264\377\262\262\262\377\261\261\261\377\256\256\256\377\221"
"\221\221\377PPP\315\0\0\0\22\0\0\0\15@@@\\PPP\341SSS\377SSS\377SSS\377"
"SSS\377SSS\377SSS\377SSS\377SSS\377SSS\377SSS\377SSS\377SSS\377SSS\377"
"SSS\377SSS\377SSS\377SSS\377SSS\377SSS\377SSS\377SSS\377SSS\377SSS\377"
"SSS\377SSS\377SSS\377PPP\337;;;V\0\0\0\12\0\0\0\0\0\0\0\16\0\0\0\36\0"
"\0\0)\0\0\0.\0\0\0""3\0\0\0""3\0\0\0""3\0\0\0""3\0\0\0""3\0\0\0""3\0"
"\0\0""3\0\0\0""3\0\0\0""3\0\0\0""3\0\0\0""3\0\0\0""3\0\0\0""3\0\0\0""3"
"\0\0\0""3\0\0\0""3\0\0\0""3\0\0\0""3\0\0\0""3\0\0\0""3\0\0\0""3\0\0\0"
"/\0\0\0/\0\0\0*\0\0\0\35\0\0\0\11\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0"
"\0\6\0\0\0\13\0\0\0\15\0\0\0\15\0\0\0\15\0\0\0\15\0\0\0\15\0\0\0\15\0"
"\0\0\15\0\0\0\15\0\0\0\15\0\0\0\15\0\0\0\15\0\0\0\15\0\0\0\15\0\0\0\15"
"\0\0\0\15\0\0\0\15\0\0\0\15\0\0\0\15\0\0\0\15\0\0\0\15\0\0\0\15\0\0\0"
"\13\0\0\0\10\0\0\0\4\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"};



+ 59
- 0
hopman-1.0/GTK2/set_icon.c View File

@@ -0,0 +1,59 @@
#include <gtk/gtk.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <stdlib.h>
#include <string.h>
#include "hopman.h"
#include "gui.h"

static guint32 u8tou32(const guint8* b)
{
guint32 u;
int i;
for(u=*b, i=1; i<4; i++)
{
u <<= 8;
u |= *(b+i);
}
return u;
}

/*============================== PIXBUF DATA ================================*/
#include "icon_32x32"

/*================= Destructor (dummy because data is static) ===============*/
void nop_destroy (guchar *pixels, gpointer data)
{
/* NO OP */
}

void set_icon(GtkWindow *pW)
{
GdkPixbuf *pPix;
const char *magic = "GdkP";
int i;
guint32 total_len, pixdata_type, width, height, rowstride;
const guint8 *pixeldata;
if( strncmp(icon_32x32, magic, 4) )
{
fprintf(stderr, "%s error in function set_icon(): invalid Gdk pixbuf.\n",
progname);
exit(EXIT_FAILURE);
}

total_len = u8tou32(icon_32x32+4);
pixdata_type = u8tou32(icon_32x32+8);
rowstride = u8tou32(icon_32x32+12);
width = u8tou32(icon_32x32+16);
height = u8tou32(icon_32x32+20);
pixeldata = icon_32x32 + 24;
/* printf("total length=%u,\npixeldata_type=0x%x,\nrowstride=%u,\n"
"width=%u, height=%u.\n", total_len, pixdata_type, rowstride,
width, height); */

pPix=gdk_pixbuf_new_from_data(pixeldata, GDK_COLORSPACE_RGB, TRUE, 8,
width, height, rowstride, nop_destroy, NULL);

gtk_window_set_icon( pW, pPix );
}

+ 36
- 0
hopman-1.0/INSTALL View File

@@ -0,0 +1,36 @@
HOPMAN. HOtplug Partition MANager

This is a tool to let user interactively, on mouse-click, mount/unmount and
open filesystems on hotplug devices such as USB memory sticks or SD memory
cards.
Copyright (C) 2019 Didier Kryn <kryn@in2p3.fr> -- Seee LICENSE
'make' builds the executable
'make clean' removes object files, keeps libraries and executable
'make install' installs executable, default config file, man page, icon and
launcher
'make cleanall' removes everything but source files and installed files
'make uninstall' removes installed files

List of installed files:
/usr/bin/hopman
/etc/default/hopmanrc
/usr/share/man/man8/hopman.8.gz \
/usr/share/pixmaps/hopman.png \
/usr/share/applications/hopman.desktop

Suggestion to installers:
make install; make cleanall

You must be root to make install/uninstall
Installation of the executable will fail if the application is running.

Installation dependencies:
- Linux kernel version newer than 2.2.26
- GTK+-2 library

Practical dependencies at run time:
- applications to mount and unmount filesystems without root permission,
e.g. pmount/pumount
- file-manager and terminal-emulator. e.g. thunar and xfce4-terminal

+ 22
- 0
hopman-1.0/LICENSE View File

@@ -0,0 +1,22 @@

Copyright (C) 2019 Didier Kryn <kryn@in2p3.fr>
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
COPYRIGHT HOLDERS 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 copyright holders 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
copyright holders.

+ 80
- 0
hopman-1.0/Makefile View File

@@ -0,0 +1,80 @@
# Copyright (C) 2019 Didier Kryn <kryn@in2p3.fr> -- See LICENSE
# 'make' builds the executable
# 'make clean' removes object files, keeps libraries and executable
# 'make install' installs executable, default config file, icon and launcher
# 'make cleanall' removes everything but source files and installed files
# 'make uninstall' removes installed files
# You must be root to make install/uninstall

SWATCH_FUNC_C = watch/partition.c watch/init_list.c watch/inot.c \
watch/sigfd.c watch/hotplug_partition.c watch/mountpoints.c \
watch/devpath.c watch/readwrite.c watch/command.c watch/start_periodic.c \
watch/epopen.c

ANNEX_FUNC_C = annex/config.c annex/userinfo.c annex/newpid.c \
annex/do_all_config.c

UI_FUNC_GTK2_C = GTK2/hopman.c GTK2/gui.c GTK2/set_icon.c GTK2/gui.h

INSTALLED_FILES = /usr/bin/hopman /usr/share/man/man8/hopman.8.gz \
/etc/default/hopmanrc /usr/share/pixmaps/hopman.png \
/usr/share/applications/hopman.desktop /usr/share/doc/hopman/copyright


.SUFFIXES:

all: GTK2/hopman

GTK2/hopman: $(UI_FUNC_GTK2_C) hopman.h GTK2/gui.h \
watch/watch.a annex/annex.a
make -C GTK2 hopman


watch/watch.a: $(WATCH_FUNC_C) watch/watch.h hopman.h
make -C watch watch.a

annex/annex.a: $(ANNEX_FUNC_C) annex/annex.h hopman.h
make -C annex annex.a

install: $(INSTALLED_FILES)

/usr/bin/hopman: GTK2/hopman
cp $< $@

/usr/share/man/man8/hopman.8.gz: doc/man/man8/hopman
gzip -c $< > $@

/etc/default/hopmanrc: annex/hopmanrc
cp $< $@

/usr/share/pixmaps/hopman.png: icon/removable-media-32x32.png
cp $< $@

/usr/share/applications/hopman.desktop: hopman.desktop
cp $< $@

/usr/share/doc/hopman/copyright: LICENSE /usr/share/doc/hopman
cp $< $@

/usr/share/doc/hopman:
mkdir $@

clean:
make -C watch clean
make -C annex clean
make -C GTK2 clean
@rm -vf $(wildcard doc/man/man8/*~)
@rm -vf $(wildcard *~)

cleanall:
make -C watch cleanall
make -C annex cleanall
make -C GTK2 cleanall
@rm -vf $(wildcard doc/man/man8/*~)
@rm -vf $(wildcard *~)

uninstall:
@rm -vf $(INSTALLED_FILES)
@rm -rvf /usr/share/doc/hopman

.PHONY: clean cleanall uninstall

+ 16
- 0
hopman-1.0/annex/Makefile View File

@@ -0,0 +1,16 @@

ANNEX_FUNC = config.o userinfo.o newpid.o do_all_config.o

%.o: %.c
gcc -c $< -I..

annex.a: $(ANNEX_FUNC)
ar -rcs $@ $^

$(ANNEX_FUNC): annex.h ../hopman.h

clean:
@rm -vf $(wildcard *.o) $(wildcard *~)

cleanall:
@rm -vf $(wildcard *.o) $(wildcard *~) annex.a

+ 23
- 0
hopman-1.0/annex/annex.h View File

@@ -0,0 +1,23 @@
/* See LICENSE file for copyright and license details. */

#if !defined(ANNEX_H)
#define ANNEX_H
#include <sys/types.h>
#include "hopman.h"

/*
The configuration data is shared between userinfo.c and config.c .
The same buffer is allocated and re-allocated. The two functions are
reallocation-safe: userinfo.c by keeping the offsets of its two strings,
instead of pointers, and config.c by keeping track of the value of
configbuf and updating its pointers in case of change. Therefore the
calling order of the two functions only matters because userinfo is
needed to determine the path of the configuration file. This is how it
has been designed, but no wild reallocation of configbuf has been tried
to check the safety of the two functions.
*/

char *configbuf;
size_t configlen;

#endif

+ 333
- 0
hopman-1.0/annex/config.c View File

@@ -0,0 +1,333 @@
/* See LICENSE file for copyright and license details. */
/*----------------------------------------------------------------------------
This file provides the following functions:
config_read(const char *filename) : parse and store configuration parameters
Low-level getters:
config_string(paramid_t) : return raw (string) parameter value
config_int(paramid_t): return integer parameter value as int
config_bool(paramid_t): return boolean parameter value as 0 or 1
High level getters:
config_pidfile(void) : return pid file name ready to open
config_helper(command_t) : return command line ready to invoke
config_show(void) : return menubar field list as bit flags
----------------------------------------------------------------------------*/
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <stdlib.h>
#include "hopman.h"
#include "annex.h"

typedef struct
{
const char *v;
size_t l;
} varinfo_t;

static varinfo_t config_param[N_CONFIG_PARAMS];

const char *oldbuf; /* save value of config buffer in case it is realloc()ed */

/*======================== DECODE CONFIGURATION FILE ========================*/
/* The configuration parameters are declared static in this file; they will */
/* be used by several functions in the same file. The Default values only */
/* exist in function config_read, and the modified values in the config file.*/
/* The varinfo_t type does not contain the text of the parameter values, but */
/* only pointers to it. We will calculate the size of the buffer necessary to*/
/* store all the parameter values, allocate it and make the pointers of the */
/* varinfo_t point into it. First, the config file will be mmap()ed. */
/* When the function returns, only the memory size needed to store the final */
/* values is retained. */

/*========= parse configuration file and store raw parameters' values =======*/
int config_read(const char *path)
{
const char *parname[N_CONFIG_PARAMS] = { PARAM_NAMES };
const char *default_param[N_CONFIG_PARAMS]= { PARAM_DEFAULTS };
char *conf=NULL;
char *b, *c, *d, *end;
size_t size, len;
int i;
/*-------------------------- Set default values ---------------------------*/
for(i=0; i<N_CONFIG_PARAMS; i++)
{
config_param[i].v = default_param[i];
config_param[i].l = strlen(default_param[i]);
}

if( !path ) goto decoding_done;
/*-------------------------- mmap the config file -------------------------*/
{
int fd, err;
off_t osize;
void *map;
fd = open(path, O_RDONLY);
if(fd == -1) return -1;
osize = lseek(fd, 0, SEEK_END);
if(osize == (off_t)-1)
{
/* close fd while preserving errno */
err = errno; close(fd); errno = err;
return -1;
}
/* We mmap the config file with two bytes more than its size because we */
/* will append '\n' and '\0' at the end to secure the decoding loop. It */
/* won't affect the file because it was opened read-only; but we must */
/* mmap it with the PROT_READ and PROT_WRITE flags */
size = (size_t)osize + 2;
map = mmap( NULL, (size_t)size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0 );
err = errno; close(fd); errno = err; /* close fd while preserving errno */
if( map == MAP_FAILED ) return -1;
conf = (char *)map;
end = conf+size;
conf[size-2] = '\n';
conf[size-1] = '\0';
}

/*------------------------ decode file content ----------------------------*/
/* We have made sure our data is finished by an end-of-line and contained */
/* in a nulll-terminated string. We can now safely loop on lines by */
/* searching end-of-lines. */
for( d=c=conf, strsep(&c, "\n"); c ; d=c, strsep(&c, "\n") )
{
int p1, p2, p3;
char *n, *v;
if( *d == '#' ) continue;
p1=p2=p3=-1;
sscanf(d, " %n%*s%n = %n%*s]", &p1, &p2, &p3);
if(p3==-1) continue;
n = d+p1;
v = d+p3;
d[p2] = '\0';
for(b=n; *b; b++) *b = tolower(*b);
for(i=0; i<N_CONFIG_PARAMS; i++)
{
if( strcmp(parname[i], n) ) continue;
/* we have found an assignment instruction to a known parameter */
config_param[i].v = v;
config_param[i].l = strlen(v);
/* the parameter value now points into the memory map of the file */
}
}
decoding_done:
/* All the const char* of our array of varinfo_t now point to either some */
/* location in the mmaped file or some constant string which exists only */
/* in this function. We need to store all of them in the long-lasting */
/* configuration buffer before we unmap the file and return from the */
/* function. */
/* The config buffer should already contain username and user's home. */
/*---------- increase buffer size to store configuration data -------------*/
/* Calculate size, including one byte per parameter string to store the \0 */
len = configlen;
for(i=0; i<N_CONFIG_PARAMS; i++) len += (config_param[i].l + 1);

if( ! (c = (char *)realloc(configbuf, len)) )
{
if(conf)
{
int err = errno;
munmap(conf, size);
errno = err;
}
return -1;
}
else oldbuf = configbuf = c; /* successfull realloc() */

/*
printf( "Buffer allocated from %p to %p (%u chars).\n",
configbuf,configbuf+len-1,len);
*/
/* copy variable values in buffer */
for(b=configbuf+configlen, i=0; i<N_CONFIG_PARAMS; i++)
{
strcpy(b, config_param[i].v);
config_param[i].v = b;
b += (config_param[i].l + 1);
}
configlen = len;

/*---------------------------- Unmap config file --------------------------*/
if(conf) munmap(conf, size);

/*
for( b=configbuf, i=0; i<len; i++, b++ ) fputc( ((*b) ? *b : '\n'), stdout);
*/
return 0;
}

/* Recalculate string pointers if config buffer has been moved by realloc() */
static void reallocate(void)
{
unsigned i;
if(configbuf == oldbuf) return;

for(i=0; i<N_CONFIG_PARAMS; i++)
config_param[i].v += (configbuf-oldbuf);
oldbuf = configbuf;
}

/*====================== parameter retrieval functions ======================*/
/* raw string */
const char *config_string(paramid_t param)
{
if(configbuf != oldbuf) reallocate();
return config_param[param].v;
}

/* boolean decoded as int */
int config_bool(paramid_t param)
{
if(configbuf != oldbuf) reallocate();
if( !strcasecmp("true", config_param[param].v) ) return 1;
else if ( !strcasecmp("false", config_param[param].v) ) return 0;
else return -1;
}

/* integer decoded as int */
int config_int(paramid_t param)
{
int i, n;
if(configbuf != oldbuf) reallocate();
n = sscanf( config_param[param].v, " %d ", &i );
if(n==1) return i;
else return -1;
}

/* pid file as string after substitution of escape sequences %[uh]*/
const char *config_pidfile(char *wbuf, size_t len)
{
const char *c, *conf;
char pc, *d, *end;

end = wbuf+len;
conf = config_string(PidFile);
if(!conf) return NULL;

for( c=conf, pc=0, d=wbuf; *c && d<end; pc=*c, c++, d++ )
{
const char *uname = username();
const char *uhome = userhome();
if(pc=='%') /* possible valid escape sequence */
{
if( *c=='u' && uname )
{
d--;
strcpy(d, uname);
d += strlen(uname)-1;
}
else if( *c=='h' && uhome)
{
d--;
strcpy(d, uhome);
d += strlen(uhome)-1;
}
else*d = *c;
}
else *d = *c;
}
*d = '\0';
return wbuf;
}

/* command helper as string after substitution of escape sequences %[lmnuh] */
const char *config_helper(command_t cmd, partinfo_t *p, char *wbuf, size_t len)
{
paramid_t pi;
const char *c, *conf;
char pc, *d, *end;
const char *uname = username();
const char *uhome = userhome();

end = wbuf + len;
switch(cmd)
{
case Open_in_File_Manager:
pi = FileManager;
break;
case Open_in_Terminal:
pi = TerminalEmulator;
break;
case Mount:
pi = MountHelper;
break;
case Unmount:
pi=UmountHelper;
break;
case Eject:
pi=EjectHelper;
break;
default: return NULL;
}

/* scan command line and substitute paramaters to escape sequences */
conf = config_string(pi);
if(!conf) return NULL;

for( c=conf, pc=0, d=wbuf; *c && d<end; pc=*c, c++, d++ )
{
if(pc=='%') /* possible valid escape sequence (hlmnu) */
{
if( *c=='n')
{
d--;
strcpy(d, p->name);
d += strlen(p->name)-1;
}
else if( *c=='l')
{
d--;
strcpy(d, p->label);
d += strlen(p->label)-1;
}
else if( *c=='m' )
{
d--;
strcpy(d, p->mnt);
d += strlen(p->mnt)-1;
}
else if( *c=='u' && uname)
{
d--;
strcpy(d, uname);
d += strlen(uname)-1;
}
else if( *c=='h' && uhome)
{
d--;
strcpy(d, uhome);
d += strlen(uhome)-1;
}
else*d = *c;
}
else *d = *c;
}
*d = '\0';
return wbuf;
}

/* return display configuration as bit flags */
unsigned config_show(void)
{
unsigned flags = 0;
if( config_bool(ShowName) == 1 ) flags |= SHOWNAME;
if( config_bool(ShowLabel) == 1 ) flags |= SHOWLABEL;
if( config_bool(ShowFstype) == 1 ) flags |= SHOWFSTYPE;
if( config_bool(ShowMountPoint) == 1 ) flags |= SHOWMNT;
if( config_bool(Sticky) == 1 ) flags |= STICKY;

return flags;
}

+ 94
- 0
hopman-1.0/annex/do_all_config.c View File

@@ -0,0 +1,94 @@
#include <stdio.h>
#include<linux/limits.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include "hopman.h"
#include "annex.h"

static int isregular(const char *path)
{
struct stat buf;
int rc;
rc = stat(path, &buf);
if(rc)
{
fprintf( stderr, "%s cannot stat %s: %s\n", progname, path,
strerror(errno) );
return 0;
}

if ( (buf.st_mode & S_IFMT) == S_IFREG ) return 1;
fprintf( stderr, "%s: %s is not a regular file.\n", progname,
path );
return 0;
}
/* Invoke userinfo(), config_read() and newpid() */
void do_all_config(void)
{
const char *rcname = "hopmanrc";
char filename[NAME_MAX+1];
const char *h;
int rc;
if( userinfo_init() )
{
fprintf(stderr, "%s cannot retrieve username and home: %s\n",
progname, strerror(errno));
}
h = userhome();

/*----------- Try to find and read user's configuration file --------------*/
rc = -1;
if(h)
{
strcpy(filename, h);
strcat(filename, "/.hopmanrc");
if(isregular(filename))
{
rc = config_read(filename);
if(rc) fprintf( stderr, "%s cannot process %s: %s\n", progname,
filename, strerror(errno) );
else fprintf( stderr, "%s using configuration from %s.\n", progname,
filename );
}
else rc = -1;
}

/*----------- If no user's configuration file, try default one ------------*/
if(rc)
{
strcpy(filename, "/etc/default/hopmanrc");
if(isregular(filename))
{
rc = config_read(filename);
if(rc) fprintf( stderr, "%s cannot process %s: %s\n", progname,
filename, strerror(errno) );
else fprintf( stderr, "%s using configuration from %s,\n", progname,
filename );
}
else rc = -1;
}
/*-------- If no configuration file at all, try built-in defaults ---------*/
if(rc)
{
rc = config_read(NULL);
if(rc)
fprintf( stderr,
"%s error setting built-in default configuration: %s\n",
progname, strerror(errno) );
else fprintf( stderr, "%s: using built-in default configuration.\n",
progname );
}
/*-------------------- If everything failed, then exit. -------------------*/
if(rc) exit(EXIT_FAILURE);
newpid();
}

+ 36
- 0
hopman-1.0/annex/hopmanrc View File

@@ -0,0 +1,36 @@
#===== Configuration file for the hot-plug partition monitoring program =====
# Parameter names and boolean values are case-insensitive
# %u denotes username
# %h denotes home directory
# %n denotes partition name - the filename in /dev - eg sdb1
# %l denotes filesystem's label
# %m denotes partition's mountpoint

#------- File to store pid of the process. User needs write permission -------
PidFile = /tmp/%u/hopman-pid
# Only %u and %h can be used in PidFile
# All directories on the path whole will be created if necessary but only
# the file will will be deleted when program terminates.

#----------------- Helper commands are executed in /bin/sh -------------------
MountHelper = exec pmount %n %l
UmountHelper = exec pumount %n
FileManager = exec thunar
TerminalEmulator = exec xfce4-terminal
# The current directory is set to the mountpoint before invoking
# FileManager or TerminalEmulator

#---------- Time interval (in seconds) to read /proc/self/mountinfo ----------
MountCheckPeriod = 5
# 0 means don't do periodic read, read it only after mount/umount action

#-------------------------- Display Configuration ----------------------------
ShowName = False
ShowLabel = False
ShowFstype = False
ShowMountpoint = True
Sticky = True

# If both ShowName and ShowLabel are false, one of them is shown, label if
# it exists, name otherwise.
# Sticky means that the window must appear on all desktops

+ 96
- 0
hopman-1.0/annex/newpid.c View File

@@ -0,0 +1,96 @@
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <linux/limits.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include "hopman.h"
#include "annex.h"

void cleanpid(int pid, void *pdata)
{
const char *fn = (const char *)pdata;
unlink( fn );
return;
}
void newpid(void)
{
FILE *pf;
int k, l, n, rc;
struct timespec milisec;
const char *fn;
pid_t pid, mypid;
static char buf[NAME_MAX+1];

milisec.tv_sec = 0;
milisec.tv_nsec = 1000000;
fn = config_pidfile(buf, NAME_MAX+1);
if( !fn )
{
fprintf(stderr, "%s: no pid-file configured.\n", progname);
return;
}

mypid = getpid();
/* try hard to kill previous instance */
for( k=rc=0; k<3 && (pf=fopen(fn, "r")); k++)
{
n = fscanf(pf, " %d ", &l);
fclose(pf);
if(n==1 && !rc)
{
pid = l;
if(pid < 2) break;
rc = kill(pid, SIGKILL);
}
nanosleep(&milisec, NULL);
}

/*---- Make sure all directories on the path exist before opening file ----*/
/* remember fn points to buf. Count slashes */
for(l=0; buf[l] == '/'; l++); /* skip leading slashes */
for(k=l, n=0; buf[k]; k++) if(buf[k] == '/') n++;
{
char *slash[n], *c;
struct stat stb;
for(k=0, c=buf+l; *c; c++) /* record slash positions */
{
if(*c=='/')
{
slash[k] = c;
k++;
}
}
for(k=0; k<n; k++) *slash[k]='\0'; /* replace all '/' by '\0' */
for(k=0; k<n; k++)
{
/* if directory doesn't exist, create it */
if( stat(buf, &stb) )
{
if(errno == ENOENT)
{
if( mkdir(buf, 0755) ) goto error;
}
else goto error;
}
*slash[k] = '/'; /* put slash back */
}
}
pf = fopen(fn, "w");
if(!pf) goto error;
fprintf(pf, "%u\n", mypid);
fclose(pf);
on_exit(cleanpid, (void *)fn); /* fn points to buf which is static */
return;

error:
fprintf(stderr, "%s can't create pid file: %s\n", progname, strerror(errno));
return;
}

+ 34
- 0
hopman-1.0/annex/tryconf.c View File

@@ -0,0 +1,34 @@
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include "hopman.h"
int config_read(const char *);
int main(int argc, char **argv)
{
char buf[1024];
const char *name[N_CONFIG_PARAMS] = {PARAM_NAMES};
partinfo_t bidon = {1234, 1, "foo5", "Tagada", "tmpfs", "/media/bar"};
userinfo_init();
if( config_read(argv[1]) )
fprintf(stderr, "Error returned by config_read(): %s\n", strerror(errno) );
else
{
paramid_t pi;
for(pi=0; pi<N_CONFIG_PARAMS; pi++)
{
const char *c = config_string(pi);
printf("%s: %p .. %p = ", name[pi], c, c+strlen(c));
if(pi<MountCheckPeriod) printf("%s\n", config_string(pi));
else if(pi == MountCheckPeriod) printf("%d\n", config_int(pi));
else if(pi == PidFile) printf("%s\n", config_string(pi));
else printf("%d\n", config_bool(pi));
}
fflush(stdout);
printf("pid file = %s\n", config_pidfile(buf, sizeof(buf)));
printf( "mount command=\"%s\"\n",
config_helper(Mount, &bidon, buf, sizeof(buf)) );
}
return 0;
}

+ 77
- 0
hopman-1.0/annex/userinfo.c View File

@@ -0,0 +1,77 @@
/* See LICENSE file for copyright and license details. */
/*----------------------------------------------------------------------------

----------------------------------------------------------------------------*/
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <pwd.h>
#include <errno.h>
#include <string.h>
#include "hopman.h"
#include "annex.h"

/* offsets of username and home in configbuf: */
int name_offset, home_offset;
/* We save offsets rather than pointers because configbuf may be realloc()ed */

/*------------------------- obtain username and home ------------------------*/
int userinfo_init(void)
{
char pwbuf[1024];
struct passwd pw, *ppw;
uid_t uid;
size_t len, lenname, lenhome;
char *newbuf;

uid = getuid();
errno = getpwuid_r(uid, &pw, pwbuf, 1024, &ppw);
if( errno )
{
name_offset = home_offset = -1;
return -1;
}
else if( !ppw)
{
name_offset = home_offset = -1;
return 0;
}

lenname = strlen(ppw->pw_name);
lenhome = strlen(ppw->pw_dir);

/* We do not assume that configbuf is empty. At startup, configbuf is NULL
and configlen is 0, but they may have been changed before userinfo_init
is invoked. */
len = configlen + lenname + lenhome + 2;
newbuf = realloc(configbuf, len);
if(!newbuf)
{
name_offset = home_offset = -1;
return -1;
}

configbuf = newbuf;

/* store username and userhome at the end of configuration buffer */
strcpy(configbuf+configlen, ppw->pw_name);
strcpy(configbuf+configlen+lenname+1, ppw->pw_dir);
name_offset = configlen;
home_offset = configlen+lenname+1;
configlen = len;
return 0;
}

const char *username(void)
{
if(name_offset == -1) return NULL;
else return configbuf + name_offset;
}

const char *userhome(void)
{
if(home_offset == -1) return NULL;
else return configbuf + home_offset;
}


+ 29
- 0
hopman-1.0/doc/man/man8/hopman View File

@@ -0,0 +1,29 @@
.\" Manpage for hopman-gtk2.
.\" Contact kryn@in2p3.fr to correct errors or typos.
.TH man 8 "19 April 2019" "1.0" "hopman-gtk2 man page"
.SH NAME
hopman-gtk2
Monitor insertion/removal of hotplug partitions, mount, unmount and open them.
.SH SYNOPSIS
hopman-gtk2 [generic Xwindow options]
.SH DESCRIPTION
This is hopman with a GTK+-2 graphic interface. The operation of hopman is
governed by its configuration file which is searched in .hopmanrc in the
user's home directory or in /etc/default/hopmanrc. Hopman shows a list of
hotplug (removable) mass storage partitions; it is invisible when there is
no hot-plug partition. The configuration file determines what is shown, but,
at least the filesystem's label or the partition's name is shown. A mouse
click on a partition opens a menu of actions allowing to mount or unmount the
filesystem contained in the partition or to open it in a file manager or a
terminal. The external commands used to mount, unmount and open directories
can be specified in the configuration file. If the configuration file defines
it, hopman stores its process-id in a file (pid-file). If a pid-file
is specified, at startup, the application kills the previous instance, if any;
on exit it removes the pid-file; for this purpose it intercepts the SIGTERM
and SIGINT signals to exit gracefully.
.SH FILES
/etc/default/hopmanrc, .hopmanrc
.SH SEE ALSO
pmount, pumount
.SH AUTHOR
Didier Kryn <kryn@in2p3.fr>

+ 11
- 0
hopman-1.0/hopman.desktop View File

@@ -0,0 +1,11 @@
[Desktop Entry]
Version=1.0
Type=Application
Name=Hopman
Categories=System;GTK
Comment=manage removable media
Exec=/usr/bin/hopman
Icon=/usr/share/pixmaps/hopman.png
Path=/dev
Terminal=false
StartupNotify=false

+ 129
- 0
hopman-1.0/hopman.h View File

@@ -0,0 +1,129 @@
/*-----------------------------------------------------------------------------
Copyright (C) 2019 Didier Kryn <kryn@in2p3.fr>
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
COPYRIGHT HOLDERS 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 copyright holders 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
copyright holders.
---------------------------------------------------------------------------*/

/*-----------------------------------------------------------------------------
This header file contains declarations common to the partition-watching
and command-submission machineries and the UI.
---------------------------------------------------------------------------*/

#if !defined(HOPMAN_H)
#define HOPMAN_H
#include <sys/types.h>
#include <linux/limits.h>
#define PM_NAME_LEN 15
#define PM_LABEL_LEN 15
#define PM_FSTYPE_LEN 15
#define PM_MNT_LEN NAME_MAX

/* interval of periodic read of /proc/self/mountinfo: */
#define PM_ITIMERVAL 5

/*--------- Declaration of the object representing a partition ---------*/
typedef
struct
{
ino_t inode;
unsigned nmounts;
char name[PM_NAME_LEN+1];
char label[PM_LABEL_LEN+1];
char fstype[PM_FSTYPE_LEN+1];
char mnt[PM_MNT_LEN+1];
void *uiptr[16]; /* A provision for the private use of the UI */
} partinfo_t;

typedef
enum {Open_in_File_Manager, Open_in_Terminal, Mount, Unmount, Eject} command_t;

/*====================== Functions the UI shall provide =====================*/
void ui_create(partinfo_t *p); /* invoked when a partition was inserted */
void ui_delete(partinfo_t *p); /* invoked when a partition was removed */
void ui_update(partinfo_t *p); /* invoked when partition status changed */
void ui_set_visible(int); /* show/hide the user interface */
void ui_msg(const char *msg); /* display a message */

/*======================= Functions the UI shall invoke =====================*/
/* By main() at start up */
int userinfo_init(void); /* At the beginning, obtain username ahd home */
/*** config_read() -- see below configuration file interface. ***/
void newpid(void); /* kill previous instance and rewrite pid file */
int inotinit(void); /* return input file descriptor to watch in main loop */
int signalinit(void); /* return input file descriptor to watch in main loop */
void start_periodic(time_t); /* start periodic ALRM signal */

/* By main() just before entering iddle loop */
void init_list(void); /* Detect hotplug partitions present at start up */

/* By UI callbacks connected to watched input file descriptors */
void inotify_read( int inotfd );
void signal_read( int sfd );

/* By UI's user action call-backs associated to menus */
void execute(command_t cmd, partinfo_t *p);


/*--------------- Interface with the configuration file --------------------*/
/* parameters:, IDs, names and default values: */
typedef enum paramid_t
{
MountHelper, UmountHelper, EjectHelper, FileManager, TerminalEmulator,
MountCheckPeriod, ShowName, ShowLabel, ShowFstype, ShowMountPoint, Sticky,
PidFile, N_CONFIG_PARAMS
} paramid_t;

#define PARAM_NAMES "mounthelper", "umounthelper", "ejecthelper", \
"filemanager", "terminalemulator", "mountcheckperiod", \
"showname", "showlabel", "showfstype", "showmountpoint", "sticky", \
"pidfile"

#define PARAM_DEFAULTS "exec pmount %n %l", "exec pumount %n", \
"" , "exec thunar", "exec xfce4-terminal", "5", \
"false", "false", "false", "true", "true", "%h/hopman-pid"

/* Comment: there is currently no known eject-helper */
int config_read(const char *path); /* return -1 in case of error, else 0 */
const char *config_string(paramid_t param);
int config_int(paramid_t param); /* -1 in case of error */
int config_bool(paramid_t param); /* 1 for "true", 0 for "false" else -1 */
/* config_bool() is case-insensitive */
unsigned config_show(void); /* provide configuration for the UI */
#define SHOWNAME 1
#define SHOWLABEL 2
#define SHOWFSTYPE 4
#define SHOWMNT 8
#define STICKY 16
/* In the 2 functions below, buf must be large enough to store the resulting
string. */
const char *config_pidfile(char *buf, size_t len);
const char *config_helper(command_t cmd, partinfo_t *p, char *buf, size_t len);
const char *username(void);
const char *userhome(void);

void do_all_config(void);/* invoke userinfo_init(), config_read() & newpid() */
/*-------------------------- Global constant --------------------------------*/
#if !defined(MAIN_PROGRAM)
extern
#endif
const char *progname; /* main() is expected to set this string */

#endif

BIN
hopman-1.0/icon/removable-media-32x32.png View File

Before After
Width: 32  |  Height: 32  |  Size: 964 B

+ 17
- 0
hopman-1.0/watch/Makefile View File

@@ -0,0 +1,17 @@

WATCH_FUNC = partition.o init_list.o inot.o sigfd.o hotplug_partition.o \
mountpoints.o devpath.o readwrite.o command.o start_periodic.o epopen.o

%.o: %.c
gcc -c $< -I..

watch.a: $(WATCH_FUNC)
ar -rcs $@ $^

$(WATCH_FUNC): watch.h ../hopman.h

clean:
@rm -vf $(wildcard *.o) $(wildcard *~)

cleanall:
@rm -vf $(wildcard *.o) $(wildcard *~) watch.a

+ 115
- 0
hopman-1.0/watch/command.c View File

@@ -0,0 +1,115 @@
#include <stdio.h>
#include <sys/types.h>
#include <wait.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include "hopman.h"
#include "watch.h"

void execute(command_t command, partinfo_t *p)
{
char cmdbuf[1024];
const char *cmdline;
FILE *pfin = NULL;
char *line;
int len;
pid_t pid;
int wstatus;
char short_command; /* if short command we wait for completion */

cmdline = config_helper( command, p, cmdbuf, sizeof(cmdbuf) );
if(!cmdline) goto nocmd;
if(!*cmdline) goto nocmd;
switch(command)
{
case Open_in_File_Manager:
case Open_in_Terminal:
if( !p->nmounts ) execute(Mount, p); /* recursion ok for short command */
pfin = epopen(cmdline, &pid, p->mnt);
short_command = 0;
break;
case Mount:
if(!p->nmounts)
{
pfin = epopen(cmdline, &pid, "");
short_command = 1;
}
else ui_msg("already mounted.");
break;
case Unmount:
if(p->nmounts)
{
pfin = epopen(cmdline, &pid, "");
short_command = 1;
}
else ui_msg("not mounted.");
break;
case Eject:
pfin = epopen(cmdline, &pid, "");
short_command = 0;
break;
default:
ui_msg("internal program error: invalid command.");
}
if(pfin && short_command)
{
line = fgets(cmdbuf, sizeof(cmdbuf), pfin);
if(line)
{
len = strlen(cmdbuf);
if(cmdbuf[len -1] == '\n') { cmdbuf[len -1] = '\0' ; len --; }
if(len) ui_msg(cmdbuf);
}
fclose(pfin);
waitpid(pid, &wstatus, 0);
}
else if(pfin) fclose(pfin);
mountpoints(); /* always update */
return;
nocmd:
ui_msg("No command helper.");
mountpoints(); /* always update */
return;
}

/* Reap long lasting child processes */
void command_finished(pid_t pid, int wstatus)
{
char message[80];

message[0] = '\0';
if(WIFEXITED(wstatus))
{
int status = WEXITSTATUS(wstatus);
switch(status)
{
case EXIT_SUCCESS:
/* Don't bother ! */
break;
case EXIT_FAILURE:
fprintf( stderr, "%s helper command with pid %u failed.\n",
progname, pid );
break;
default:
fprintf(stderr,
"%s helper command with pid %u terminated with status %d.\n",
progname, pid, status );
}
}
else if(WIFSIGNALED(wstatus))
{
int signal = WTERMSIG(wstatus);
fprintf( stderr,
"%s helper command with pid %u terminated by signal %d.\n",
progname, pid, signal );
}
else if(WIFSTOPPED(wstatus))
{
int signal = WSTOPSIG(wstatus);
fprintf( stderr, "%s helper command with pid %u stopped by signal %d\n",
progname, pid, signal );
}
}

+ 80
- 0
hopman-1.0/watch/devpath.c View File

@@ -0,0 +1,80 @@
/* See LICENSE file for copyright and license details. */
/* browse /sys/devices to find a subdirectory with the name of a device */
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include "watch.h"

/* hidden function which does all the work: */
static char *digsys(const char *, const char *, char *namebuf);

/*---------------------- The caller's interface -------------------------*/
/* Warning: the size of namebuf is unknown to the function. Better provide
NAME_MAX+1 bytes. NAME_MAX+1 is defined in limits.h and dirent.h */
char *devpath(const char *devname, char *namebuf)
{
return digsys("/sys/devices", devname, namebuf);
}

/* dig recursively into pathname to find a subdir named findname */
/* we use lstat because we don't want to dereference symlinks */
static char *digsys( const char *pathname, const char *findname,
char namebuf[NAME_MAX+1] )
{
struct stat mystat;
char *foundname;
DIR *dirp;
struct dirent *bdir;
struct stat bstat;
int pathsize;

foundname = NULL;
if( lstat(pathname, &mystat) ) return NULL;
/* Only consider directories */
if ( ! S_ISDIR(mystat.st_mode) ) return NULL;
dirp = opendir ( pathname );
if( !dirp )
{
fprintf( stderr, "Cannot open %s: %s\n", pathname, strerror(errno) );
exit(EXIT_FAILURE);
}
while ( (bdir=readdir(dirp)) )
{
if (!strcmp(bdir->d_name, ".") || !strcmp(bdir->d_name, "..") )
continue;
pathsize = strlen(pathname) + strlen(bdir->d_name) + 2;
{
char newpath[pathsize];
strcpy(newpath, pathname);
strcat(newpath, "/");
strcat(newpath, bdir->d_name);
lstat (newpath, &bstat);
/* only consider directories */
if (S_ISDIR(bstat.st_mode) )
{
if( !strcmp(bdir->d_name, findname) )
{
/* BINGO ! */
strcpy(namebuf, pathname);
strcat(namebuf, "/");
strcat(namebuf, findname);
foundname = namebuf;
break;
}
else
{
/* otherwise invoke ourself recursively */
foundname = digsys ( newpath, findname, namebuf );
if(foundname) break;
}
}
}
}
closedir(dirp);
return foundname;
}

+ 56
- 0
hopman-1.0/watch/epopen.c View File

@@ -0,0 +1,56 @@
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "watch.h"

/*====== A subprogram like popen() but with diferences explained below ======*/
/* The pipe is to read in the current process and connected to stderr in the */
/* new one. The intent is to report error messages. Plus pid is returned. */
/* If dirpath is an absolute path, chdir to it prior to excuting command. */
FILE *epopen( const char *command, pid_t *pid , const char *dirpath)
{
int pipefd[2];
pid_t p_id;
FILE *pf;
int rc;

/*--------------------------- Create the pipe -----------------------------*/
if(pipe(pipefd))
{
*pid = -1;
return NULL;
}

/*------------------------------------ fork -------------------------------*/
p_id = fork();
switch(p_id)
{
case 0: /* the child */
close(pipefd[0]);
close(STDERR_FILENO);
dup2(pipefd[1], STDERR_FILENO);
close(pipefd[1]);
if( *dirpath == '/' ) chdir( dirpath );
/* restore original mask */
if( sigprocmask(SIG_SETMASK, &oldset, NULL) )
fprintf( stderr, "Error detected by sigprocmask(): %s\n",
strerror(errno) );
execl( "/bin/sh", "sh", "-c", command, (char *)NULL );
fprintf( stderr, "Error invoking /bin/sh: %s\n", strerror(errno) );
exit(EXIT_FAILURE); /* reminder: we are the child */
case -1: /* fork() failed ! */
rc = errno;
close(pipefd[0]);
close(pipefd[1]);
errno = rc;
*pid = -1;
return NULL;
default: /* the parent */
close(pipefd[1]);
pf = fdopen(pipefd[0], "r");
*pid = p_id;
return pf; /* The caller must close the pipe after use */
}
}

+ 35
- 0
hopman-1.0/watch/hotplug_partition.c View File

@@ -0,0 +1,35 @@
/* See LICENSE file for copyright and license details. */
/* Check if a device is a partition and its disk is removable */
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <limits.h>
#include "watch.h"

int hotplug_partition(const char *name)
{
char namebuf[NAME_MAX+1], *pathname, c;
int fd, devfd; /* file descriptors */
struct stat buf;
int removable = 0;
if( !(pathname=devpath(name, namebuf)) ) goto close0;
/* open directory pointed to by pathname */
if( (devfd=open(pathname, O_RDONLY)) < 0 ) goto close0;

/* check the directory c