1pass.c
author Ryan C. Gordon <icculus@icculus.org>
Fri, 23 Jun 2017 17:28:03 -0400
changeset 58 1390348facc7
parent 56 a573346e6f7b
permissions -rw-r--r--
Command line tool that decrypts an OPVault keychain and dumps it to stdout.

To compile: gcc -o opvault opvault.c cJSON.c -lcrypto

Usage: ./opvault </path/to/mykeychain.opvault> <password>

This is just a proof of concept; I'll be recycling this into proper OPVault
support in 1pass later and deleting this tool.

This uses OpenSSL's libcrypto for the math instead of all the homegrown
crypto this project is otherwise using. I'll probably migrate the rest in
this direction, too, since this wasn't as bad as I expected to use and
gets you all the package-manager mojo of automatic bug fixes and security
patches and shared code, etc.

cJSON parses JSON in C. That is from https://github.com/DaveGamble/cJSON

An example OPVault keychain from AgileBits is available here:

https://cache.agilebits.com/security-kb/
icculus@28
     1
#include <linux/input.h>
icculus@28
     2
#include <fcntl.h>
icculus@0
     3
#include <stdio.h>
icculus@0
     4
#include <stdlib.h>
icculus@0
     5
#include <string.h>
icculus@0
     6
#include <assert.h>
icculus@28
     7
#include <errno.h>
icculus@28
     8
#include <unistd.h>
icculus@31
     9
#include <dirent.h>
icculus@33
    10
#include <signal.h>
icculus@46
    11
#include <mntent.h>
icculus@0
    12
#include "lua.h"
icculus@0
    13
#include "lauxlib.h"
icculus@0
    14
#include "lualib.h"
icculus@0
    15
#include "pkcs5_pbkdf2.h"
icculus@0
    16
#include "aes.h"
icculus@0
    17
#include "base64.h"
icculus@0
    18
#include "md5.h"
icculus@46
    19
#include "sha256.h"
icculus@17
    20
#include "keyhook.h"
icculus@56
    21
#include "otp.h"
icculus@45
    22
icculus@11
    23
#include <gtk/gtk.h>
icculus@45
    24
#include <gdk/gdk.h>
icculus@45
    25
#include <gdk/gdkx.h>
icculus@45
    26
#include <gdk/gdkkeysyms.h>
icculus@45
    27
#include <X11/Xlib.h>
icculus@45
    28
#include <X11/Xlibint.h>
icculus@0
    29
icculus@0
    30
#define STATICARRAYLEN(x) ( (sizeof ((x))) / (sizeof ((x)[0])) )
icculus@0
    31
icculus@28
    32
// plug in a Griffin Powermate, make sure you have access to it, and run with
icculus@28
    33
//  --powermate=/dev/input/eventX
icculus@28
    34
static int powermate_fd = -1;
icculus@28
    35
static int pumpPowermate(void)
icculus@28
    36
{
icculus@28
    37
    struct input_event buf[32];
icculus@28
    38
    int pressed = 0;
icculus@28
    39
    ssize_t br;
icculus@28
    40
icculus@28
    41
    if (powermate_fd == -1)
icculus@28
    42
        return 0;  // nothing to do.
icculus@28
    43
icculus@28
    44
    while ((br = read(powermate_fd, buf, sizeof (buf))) > 0)
icculus@28
    45
    {
icculus@28
    46
        ssize_t i;
icculus@28
    47
        br /= sizeof (buf[0]);
icculus@28
    48
        for (i = 0; i < br; i++)
icculus@28
    49
        {
icculus@28
    50
            struct input_event *ev = &buf[i];
icculus@28
    51
            if ((ev->type == EV_KEY) && (ev->code == BTN_0) && (ev->value))
icculus@28
    52
                pressed = 1;
icculus@28
    53
        } // for
icculus@28
    54
    } // while
icculus@28
    55
icculus@28
    56
    return pressed;
icculus@28
    57
}
icculus@28
    58
icculus@28
    59
static void setPowermateLED(const int enable)
icculus@28
    60
{
icculus@28
    61
    struct input_event ev;
icculus@28
    62
    const int brightness = enable ? 255 : 0;
icculus@28
    63
    const int pulse_speed = 255;
icculus@28
    64
    const int pulse_table = 0;
icculus@28
    65
    const int pulse_awake = enable ? 1 : 0;
icculus@28
    66
    const int pulse_asleep = 0;
icculus@28
    67
icculus@28
    68
    if (powermate_fd == -1)
icculus@28
    69
        return;
icculus@28
    70
icculus@28
    71
    memset(&ev, '\0', sizeof (ev));
icculus@28
    72
    ev.type = EV_MSC;
icculus@28
    73
    ev.code = MSC_PULSELED;
icculus@28
    74
    ev.value = brightness | (pulse_speed << 8) | (pulse_table << 17) | (pulse_asleep << 19) | (pulse_awake << 20);
icculus@28
    75
icculus@28
    76
    if (write(powermate_fd, &ev, sizeof (ev)) != sizeof (ev))
icculus@28
    77
        fprintf(stderr, "WARNING: tried to set Powermate LED and failed: %s\n", strerror(errno));
icculus@28
    78
} // setPowermateLED
icculus@28
    79
icculus@28
    80
icculus@31
    81
static int openPowermate(const char *fname)
icculus@31
    82
{
icculus@31
    83
    static const char const *known_names[] = {
icculus@31
    84
        "Griffin PowerMate", "Griffin SoundKnob"
icculus@31
    85
    };
icculus@31
    86
icculus@31
    87
    char buf[255];
icculus@31
    88
    int ok = 0;
icculus@31
    89
    int fd;
icculus@31
    90
    int i;
icculus@31
    91
icculus@31
    92
    if (!fname)
icculus@31
    93
        return -1;
icculus@31
    94
icculus@31
    95
    if ((fd = open(fname, O_RDWR)) == -1)
icculus@31
    96
        fprintf(stderr, "WARNING: couldn't open Powermate at %s: %s\n", fname, strerror(errno));
icculus@31
    97
icculus@31
    98
    if (ioctl(fd, EVIOCGNAME(sizeof (buf)), buf) == -1)
icculus@31
    99
    {
icculus@31
   100
        fprintf(stderr, "EVIOCGNAME failed for %s: %s\n", fname, strerror(errno));
icculus@31
   101
        close(fd);
icculus@31
   102
        return -1;
icculus@31
   103
    } // if
icculus@31
   104
icculus@31
   105
    for (i = 0; !ok && (i < sizeof (known_names) / sizeof (known_names[0])); i++)
icculus@31
   106
    {
icculus@31
   107
        if (strncmp(buf, known_names[i], strlen(known_names[i])) == 0)
icculus@31
   108
            ok = 1;
icculus@31
   109
    } // for
icculus@31
   110
icculus@31
   111
    if (!ok)
icculus@31
   112
    {
icculus@31
   113
        close(fd);
icculus@31
   114
        return -1;
icculus@31
   115
    } // if
icculus@31
   116
icculus@31
   117
    fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
icculus@31
   118
    return fd;
icculus@31
   119
} // openPowermate
icculus@31
   120
icculus@31
   121
icculus@31
   122
static void deinitPowermate(void)
icculus@31
   123
{
icculus@34
   124
    if (powermate_fd != -1)
icculus@31
   125
    {
icculus@31
   126
        setPowermateLED(0);
icculus@31
   127
        close(powermate_fd);
icculus@31
   128
        powermate_fd = -1;
icculus@31
   129
    } // if
icculus@31
   130
} // deinitPowermate
icculus@31
   131
icculus@31
   132
icculus@28
   133
static void initPowermate(int *_argc, char **argv)
icculus@28
   134
{
icculus@28
   135
    const char *arg = "--powermate=";
icculus@28
   136
    const size_t arglen = strlen(arg);
icculus@28
   137
    int argc = *_argc;
icculus@28
   138
    int i;
icculus@28
   139
icculus@28
   140
    for (i = 1; i < argc; i++)
icculus@28
   141
    {
icculus@28
   142
        const char *thisarg = argv[i];
icculus@28
   143
        if (strncmp(thisarg, arg, arglen) != 0)
icculus@28
   144
            continue;
icculus@28
   145
icculus@28
   146
        thisarg += arglen;
icculus@31
   147
icculus@31
   148
        if (strcmp(thisarg, "auto") == 0)
icculus@28
   149
        {
icculus@31
   150
            DIR *dirp = opendir("/dev/input");
icculus@31
   151
            if (dirp)
icculus@31
   152
            {
icculus@31
   153
                struct dirent *dent;
icculus@31
   154
                while ((dent = readdir(dirp)) != NULL)
icculus@31
   155
                {
icculus@31
   156
                    const char *name = dent->d_name;
icculus@31
   157
                    char buf[PATH_MAX];
icculus@31
   158
                    if (strncmp(name, "event", 5) != 0)
icculus@31
   159
                        continue;
icculus@31
   160
                    snprintf(buf, sizeof (buf), "/dev/input/%s", name);
icculus@31
   161
                    if (powermate_fd == -1)
icculus@31
   162
                    {
icculus@31
   163
                        powermate_fd = openPowermate(buf);
icculus@31
   164
                        if (powermate_fd != -1)
icculus@31
   165
                        {
icculus@31
   166
                            printf("Found Powermate at %s\n", buf);
icculus@31
   167
                            break;
icculus@31
   168
                        } // if
icculus@31
   169
                    } // if
icculus@31
   170
                } // while
icculus@31
   171
                closedir(dirp);
icculus@31
   172
            } // if
icculus@31
   173
            thisarg = NULL;
icculus@31
   174
icculus@31
   175
        } // if
icculus@28
   176
icculus@28
   177
        // eliminate this command line.
icculus@28
   178
        memmove(&argv[i], &argv[i+1], (argc-i) * sizeof (char *));
icculus@28
   179
        argc--;
icculus@31
   180
icculus@31
   181
        if (powermate_fd == -1)
icculus@31
   182
            powermate_fd = openPowermate(thisarg);
icculus@28
   183
    } // for
icculus@28
   184
icculus@31
   185
    atexit(deinitPowermate);
icculus@31
   186
icculus@28
   187
    *_argc = argc;
icculus@28
   188
} // initPowermate
icculus@28
   189
icculus@28
   190
icculus@0
   191
static lua_State *luaState = NULL;
icculus@0
   192
static const uint8_t zero16[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
icculus@0
   193
static const char saltprefix[] = { 'S', 'a', 'l', 't', 'e', 'd', '_', '_' };
icculus@0
   194
icculus@45
   195
static int makeLuaCallback(lua_State *L, const int idx)
icculus@45
   196
{
icculus@45
   197
    assert(lua_isfunction(L, idx));
icculus@45
   198
    lua_pushvalue(L, idx);  // copy the Lua callback (luaL_ref() pops it).
icculus@45
   199
    return luaL_ref(L, LUA_REGISTRYINDEX);
icculus@45
   200
} // makeLuaCallback
icculus@45
   201
icculus@0
   202
static inline int retvalStringBytes(lua_State *L, const uint8_t *str, size_t len)
icculus@0
   203
{
icculus@0
   204
    if (str != NULL)
icculus@0
   205
        lua_pushlstring(L, (const char *) str, len);
icculus@0
   206
    else
icculus@0
   207
        lua_pushnil(L);
icculus@0
   208
    return 1;
icculus@0
   209
} // retvalStringBytes
icculus@0
   210
icculus@12
   211
static inline int retvalString(lua_State *L, const char *str)
icculus@12
   212
{
icculus@15
   213
    return retvalStringBytes(L, (const uint8_t *) str, str ? strlen(str) : 0);
icculus@12
   214
} // retvalString
icculus@12
   215
icculus@11
   216
static inline int retvalPointer(lua_State *L, void *ptr)
icculus@11
   217
{
icculus@11
   218
    if (ptr != NULL)
icculus@11
   219
        lua_pushlightuserdata(L, ptr);
icculus@11
   220
    else
icculus@11
   221
        lua_pushnil(L);
icculus@11
   222
    return 1;
icculus@11
   223
} // retvalPointer
icculus@11
   224
icculus@0
   225
static inline void xorBlock(uint8_t *dst, const uint8_t *src)
icculus@0
   226
{
icculus@0
   227
    int i;
icculus@0
   228
    for (i = 0; i < 16; i++, dst++, src++)
icculus@0
   229
        *dst ^= *src;
icculus@0
   230
} // xorBlock
icculus@0
   231
icculus@0
   232
static int decryptUsingKeyAndIvec(uint8_t *data, size_t *datalen, const uint8_t *key, const uint8_t *iv)
icculus@0
   233
{
icculus@0
   234
    const size_t blocks = *datalen / 16;
icculus@0
   235
    uint8_t *block = data + ((blocks-1) * 16);   // start at final block, work backwards
icculus@0
   236
    const uint8_t *padding = &block[15];
icculus@0
   237
    uint8_t expkey[aesExpandedKeySize];
icculus@0
   238
    size_t i;
icculus@0
   239
icculus@0
   240
    if (blocks == 0)
icculus@0
   241
        return 1;  // nothing to do.
icculus@0
   242
icculus@0
   243
	aesExpandKey(key, expkey);
icculus@0
   244
icculus@0
   245
    for (i = 0; i < blocks-1; i++)
icculus@0
   246
    {
icculus@0
   247
        aesDecrypt(block, expkey, block);   // decrypt in place.
icculus@0
   248
        xorBlock(block, block-16);
icculus@0
   249
        block -= 16;
icculus@0
   250
    }
icculus@0
   251
    aesDecrypt(block, expkey, block);   // decrypt in place.
icculus@0
   252
    xorBlock(block, iv);   // xor against initial vector for final block.
icculus@0
   253
icculus@0
   254
    if (*padding > 16)
icculus@0
   255
        return 0;  // bad data?
icculus@0
   256
icculus@0
   257
    *datalen -= *padding;
icculus@0
   258
icculus@0
   259
    return 1;
icculus@0
   260
} // decryptBinaryUsingKeyAndIvec
icculus@0
   261
icculus@0
   262
icculus@0
   263
static inline int isSalted(const uint8_t *data, const size_t datalen)
icculus@0
   264
{
icculus@0
   265
    return ( (datalen > sizeof (saltprefix)) &&
icculus@0
   266
             (memcmp(data, saltprefix, sizeof (saltprefix)) == 0) );
icculus@0
   267
} // isSalted
icculus@0
   268
icculus@0
   269
icculus@0
   270
static int decryptUsingPBKDF2(lua_State *L)
icculus@0
   271
{
icculus@0
   272
    const char *base64 = luaL_checkstring(L, 1);
icculus@0
   273
    const char *password = luaL_checkstring(L, 2);
icculus@0
   274
    const int iterations = luaL_checkinteger(L, 3);
icculus@0
   275
    size_t datalen = strlen(base64);
icculus@0
   276
    uint8_t *dataptr = (uint8_t *) malloc(datalen);
icculus@0
   277
    uint8_t *data = dataptr;
icculus@0
   278
    base64_decodestate base64state;
icculus@0
   279
icculus@0
   280
    base64_init_decodestate(&base64state);
icculus@0
   281
    datalen = base64_decode_block(base64, (int) datalen, data, &base64state);
icculus@0
   282
icculus@0
   283
    const uint8_t *salt = zero16;
icculus@0
   284
    int saltlen = sizeof (zero16);
icculus@0
   285
    if (isSalted(data, datalen))
icculus@0
   286
    {
icculus@0
   287
        salt = data + 8;
icculus@0
   288
        saltlen = 8;
icculus@0
   289
        data += 16;
icculus@0
   290
        datalen -= 16;
icculus@0
   291
    } // if
icculus@0
   292
icculus@0
   293
    uint8_t output[32];
icculus@0
   294
    pkcs5_pbkdf2(password, strlen(password), salt, saltlen, output, sizeof (output), (unsigned int) iterations);
icculus@0
   295
icculus@0
   296
    const uint8_t *aeskey = &output[0];
icculus@0
   297
    const uint8_t *aesiv = &output[16];
icculus@0
   298
	if (decryptUsingKeyAndIvec(data, &datalen, aeskey, aesiv))
icculus@0
   299
        retvalStringBytes(L, data, datalen);
icculus@0
   300
    else
icculus@0
   301
        lua_pushnil(L);
icculus@0
   302
icculus@0
   303
    free(dataptr);
icculus@0
   304
    return 1;
icculus@0
   305
} // decryptUsingPBKDF2
icculus@0
   306
icculus@0
   307
icculus@0
   308
static int decryptBase64UsingKey(lua_State *L)
icculus@0
   309
{
icculus@0
   310
    size_t keylen = 0;
icculus@0
   311
    const char *base64 = luaL_checkstring(L, 1);
icculus@0
   312
    const uint8_t *key = (const uint8_t *) luaL_checklstring(L, 2, &keylen);
icculus@0
   313
    size_t datalen = strlen(base64);
icculus@0
   314
    uint8_t *dataptr = (uint8_t *) malloc(datalen);
icculus@0
   315
    uint8_t *data = dataptr;
icculus@0
   316
    base64_decodestate base64state;
icculus@0
   317
icculus@0
   318
    base64_init_decodestate(&base64state);
icculus@0
   319
    datalen = base64_decode_block(base64, (int) datalen, data, &base64state);
icculus@0
   320
icculus@0
   321
    uint8_t aeskey[16];
icculus@0
   322
    uint8_t aesiv[16];
icculus@0
   323
    MD5_CTX md5;
icculus@0
   324
icculus@0
   325
    if (isSalted(data, datalen))
icculus@0
   326
    {
icculus@0
   327
        const uint8_t *salt = data + 8;
icculus@0
   328
        const size_t saltlen = 8;
icculus@0
   329
        data += 16;
icculus@0
   330
        datalen -= 16;
icculus@0
   331
icculus@0
   332
        assert(aesNr == 10);  // AES-256 needs more rounds.
icculus@0
   333
        assert(aesNk == 4);   // hashing size is hardcoded later.
icculus@0
   334
        uint8_t hashing[32];
icculus@0
   335
icculus@0
   336
        MD5_init(&md5);
icculus@0
   337
        MD5_append(&md5, key, keylen);
icculus@0
   338
        MD5_append(&md5, salt, saltlen);
icculus@0
   339
        MD5_finish(&md5, hashing);
icculus@0
   340
icculus@0
   341
        MD5_init(&md5);
icculus@0
   342
        MD5_append(&md5, hashing, 16);
icculus@0
   343
        MD5_append(&md5, key, keylen);
icculus@0
   344
        MD5_append(&md5, salt, saltlen);
icculus@0
   345
        MD5_finish(&md5, &hashing[16]);
icculus@0
   346
icculus@0
   347
        memcpy(aeskey, hashing, 4 * aesNk);
icculus@0
   348
        memcpy(aesiv, &hashing[4 * aesNk], 16);
icculus@0
   349
    } // if
icculus@0
   350
    else
icculus@0
   351
    {
icculus@0
   352
        MD5_init(&md5);
icculus@0
   353
        MD5_append(&md5, key, keylen);
icculus@0
   354
        MD5_finish(&md5, aeskey);
icculus@0
   355
        memset(aesiv, '\0', sizeof (aesiv));
icculus@0
   356
    } // else
icculus@0
   357
icculus@0
   358
	if (decryptUsingKeyAndIvec(data, &datalen, aeskey, aesiv))
icculus@0
   359
        retvalStringBytes(L, data, datalen);
icculus@0
   360
    else
icculus@0
   361
        lua_pushnil(L);
icculus@0
   362
icculus@0
   363
    free(dataptr);
icculus@0
   364
    return 1;
icculus@0
   365
} // decryptBase64UsingKey
icculus@0
   366
icculus@0
   367
icculus@56
   368
static int decryptTopt(lua_State *L)
icculus@56
   369
{
icculus@56
   370
    const char *base32_secret = luaL_checkstring(L, 1);
icculus@56
   371
    char otpstr[16];
icculus@56
   372
    const int rc = totp(base32_secret, otpstr, sizeof (otpstr));
icculus@56
   373
    return retvalString(L, (rc == -1) ? NULL : otpstr);
icculus@56
   374
} // decryptTopt
icculus@56
   375
icculus@56
   376
icculus@46
   377
static void calcSha256(const BYTE *buf, const size_t len, BYTE *hash)
icculus@46
   378
{
icculus@46
   379
    SHA256_CTX sha256;
icculus@46
   380
    sha256_init(&sha256);
icculus@46
   381
    sha256_update(&sha256, buf, len);
icculus@46
   382
    sha256_final(&sha256, hash);
icculus@46
   383
} // calcSha256
icculus@46
   384
icculus@46
   385
static int calcSha256_Lua(lua_State *L)
icculus@46
   386
{
icculus@46
   387
    size_t len = 0;
icculus@46
   388
    const char *str = luaL_checklstring(L, 1, &len);
icculus@46
   389
    BYTE hash[32];
icculus@46
   390
    calcSha256(str, len, hash);
icculus@46
   391
    return retvalStringBytes(L, hash, sizeof (hash));
icculus@46
   392
} // calcSha256_Lua
icculus@46
   393
icculus@46
   394
icculus@12
   395
static int runGuiPasswordPrompt(lua_State *L)
icculus@12
   396
{
icculus@12
   397
    const char *hintstr = lua_tostring(L, 1);
icculus@12
   398
    GtkWidget *dialog = gtk_dialog_new_with_buttons(
icculus@12
   399
                            "Master Password", NULL, GTK_DIALOG_MODAL,
icculus@12
   400
                            GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
icculus@12
   401
                            GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
icculus@12
   402
                            NULL);
icculus@12
   403
icculus@12
   404
    GtkWidget *content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
icculus@12
   405
icculus@12
   406
    if (hintstr != NULL)
icculus@12
   407
    {
icculus@12
   408
        GtkWidget *label = gtk_label_new(hintstr);
icculus@12
   409
        gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
icculus@12
   410
        gtk_container_add(GTK_CONTAINER(content_area), label);
icculus@12
   411
    } // if
icculus@12
   412
icculus@12
   413
    GtkWidget *entry = gtk_entry_new();
icculus@12
   414
    gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
icculus@12
   415
    gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
icculus@12
   416
    gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
icculus@12
   417
    gtk_container_add(GTK_CONTAINER(content_area), entry);
icculus@12
   418
icculus@12
   419
    gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
icculus@45
   420
    gtk_window_set_keep_above(GTK_WINDOW(dialog), TRUE);
icculus@45
   421
    gtk_window_set_skip_taskbar_hint(GTK_WINDOW(dialog), TRUE);
icculus@45
   422
    gtk_window_set_skip_pager_hint(GTK_WINDOW(dialog), TRUE);
icculus@12
   423
    gtk_widget_show_all(dialog);
icculus@26
   424
    gtk_window_set_keep_above(GTK_WINDOW(dialog), TRUE);
icculus@12
   425
    const int ok = (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT);
icculus@12
   426
    retvalString(L, ok ? (const char *) gtk_entry_get_text(GTK_ENTRY(entry)) : NULL);
icculus@12
   427
    gtk_widget_destroy(dialog);
icculus@12
   428
icculus@12
   429
    return 1;
icculus@12
   430
} // runGuiPasswordPrompt
icculus@12
   431
icculus@12
   432
icculus@12
   433
static int copyToClipboard(lua_State *L)
icculus@12
   434
{
icculus@12
   435
    const char *str = luaL_checkstring(L, 1);
icculus@12
   436
    gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY), str, -1);
icculus@12
   437
    gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), str, -1);
icculus@12
   438
} // copyToClipboard
icculus@12
   439
icculus@45
   440
static gboolean checkForEscapeKey(GtkWidget *widget, GdkEvent *event, gpointer arg)
icculus@45
   441
{
icculus@45
   442
    if ((event->type == GDK_KEY_PRESS) && (event->key.keyval == GDK_KEY_Escape))
icculus@45
   443
    {
icculus@45
   444
        // !!! FIXME: this is a little hacky
icculus@45
   445
        lua_getglobal(luaState, "escapePressed");
icculus@45
   446
        lua_call(luaState, 0, 0);
icculus@45
   447
        return TRUE;
icculus@45
   448
    } // if
icculus@12
   449
icculus@45
   450
    return FALSE;  // pass this to other event handlers.
icculus@45
   451
} // checkForEscapeKey
icculus@45
   452
icculus@45
   453
static gboolean wasSearchDeleteText = FALSE;  // HACK to workaround gtk+ nonsense.
icculus@45
   454
static void searchChanged(GtkEditable *editable, gpointer arg)
icculus@11
   455
{
icculus@45
   456
    const int callback = (int) ((size_t)arg);
icculus@45
   457
    GtkWidget *vbox = gtk_widget_get_parent(GTK_WIDGET(editable));
icculus@45
   458
    lua_rawgeti(luaState, LUA_REGISTRYINDEX, callback);
icculus@45
   459
    lua_pushlightuserdata(luaState, vbox);
icculus@45
   460
    lua_pushstring(luaState, gtk_entry_get_text(GTK_ENTRY(editable)));
icculus@45
   461
    lua_call(luaState, 2, 0);
icculus@45
   462
    gtk_widget_grab_focus(GTK_WIDGET(editable));
icculus@45
   463
    if (wasSearchDeleteText)  // HACK to workaround gtk+ nonsense.
icculus@45
   464
    {
icculus@45
   465
        gtk_editable_set_position(editable, -1);
icculus@45
   466
        wasSearchDeleteText = FALSE;
icculus@45
   467
    } // if
icculus@45
   468
} // searchChanged
icculus@11
   469
icculus@45
   470
// HACK to workaround gtk+ nonsense.
icculus@45
   471
static void searchDeleteText(GtkEditable *editable, gint start_pos, gint end_pos, gpointer user_data)
icculus@45
   472
{
icculus@45
   473
    wasSearchDeleteText = TRUE;
icculus@45
   474
} // searchDeleteText
icculus@11
   475
icculus@45
   476
static void destroyLuaCallback(const int callback)
icculus@11
   477
{
icculus@45
   478
    //printf("unref callback %d\n", callback);
icculus@45
   479
    luaL_unref(luaState, LUA_REGISTRYINDEX, callback);
icculus@45
   480
} // destroyLuaCallback
icculus@45
   481
icculus@45
   482
static void destroyCallback(GtkWidget *widget, gpointer arg)
icculus@45
   483
{
icculus@45
   484
    destroyLuaCallback((int) ((size_t)arg));
icculus@45
   485
} // destroyCallback
icculus@45
   486
icculus@45
   487
static void destroyTopLevelMenu(GtkWidget *widget, gpointer arg)
icculus@45
   488
{
icculus@45
   489
    // !!! FIXME: hack
icculus@45
   490
    int *cbs = (int *) arg;
icculus@45
   491
    lua_rawgeti(luaState, LUA_REGISTRYINDEX, cbs[1]);
icculus@11
   492
    lua_call(luaState, 0, 0);
icculus@45
   493
    destroyLuaCallback(cbs[0]);
icculus@45
   494
    destroyLuaCallback(cbs[1]);
icculus@45
   495
    free(cbs);
icculus@45
   496
} // destroyTopLevelMenu
icculus@45
   497
icculus@45
   498
#if 0
icculus@45
   499
static gboolean
icculus@45
   500
mappedWindow(GtkWidget *widget, GdkEvent *event, gpointer user_data)
icculus@45
   501
{
icculus@45
   502
    GdkEventClient e;
icculus@45
   503
    memset(&e, '\0', sizeof (e));
icculus@45
   504
    e.type = GDK_CLIENT_EVENT;
icculus@45
   505
    e.window = gtk_widget_get_window(widget);
icculus@45
   506
    e.send_event = 1;
icculus@45
   507
    e.message_type = gdk_atom_intern_static_string("_NET_ACTIVE_WINDOW");
icculus@45
   508
    e.data_format = 32;
icculus@45
   509
    e.data.l[0] = 1;
icculus@45
   510
    e.data.l[1] = (long) gdk_x11_get_server_time(e.window);
icculus@45
   511
    e.data.l[2] = 0;
icculus@45
   512
icculus@45
   513
    gdk_window_raise (e.window);
icculus@45
   514
    gdk_event_send_client_message((GdkEvent *) &e, gdk_x11_drawable_get_xid(gtk_widget_get_root_window(widget)));
icculus@45
   515
    return TRUE;
icculus@45
   516
}
icculus@45
   517
#endif
icculus@45
   518
icculus@45
   519
static int guiCreateTopLevelMenu(lua_State *L)
icculus@45
   520
{
icculus@45
   521
    const char *title = luaL_checkstring(L, 1);
icculus@45
   522
    const int changedCallback = makeLuaCallback(L, 2);
icculus@45
   523
    const int destroyedCallback = makeLuaCallback(L, 3);
icculus@45
   524
icculus@45
   525
    int *cbs = (int *) malloc(sizeof (int) * 2);  // !!! FIXME: hack
icculus@45
   526
    cbs[0] = changedCallback;
icculus@45
   527
    cbs[1] = destroyedCallback;
icculus@45
   528
icculus@45
   529
    GtkWindow *window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
icculus@45
   530
    gtk_window_set_keep_above(window, TRUE);
icculus@45
   531
    gtk_window_set_skip_taskbar_hint(window, TRUE);
icculus@45
   532
    gtk_window_set_skip_pager_hint(window, TRUE);
icculus@45
   533
    g_signal_connect(window, "destroy", G_CALLBACK(destroyTopLevelMenu), cbs);
icculus@45
   534
    gtk_window_set_title(window, title);
icculus@45
   535
    gtk_window_set_position(window, GTK_WIN_POS_MOUSE);
icculus@45
   536
    gtk_window_set_decorated(window, FALSE);
icculus@45
   537
    gtk_window_set_resizable(window, FALSE);
icculus@45
   538
    GtkEntry *search = GTK_ENTRY(gtk_entry_new());
icculus@45
   539
    g_signal_connect(search, "key-press-event", G_CALLBACK(checkForEscapeKey), window);
icculus@45
   540
    gtk_entry_set_text(search, "Search...");
icculus@45
   541
    g_signal_connect(search, "changed", G_CALLBACK(searchChanged), (gpointer) ((size_t)changedCallback));
icculus@45
   542
    g_signal_connect(search, "delete-text", G_CALLBACK(searchDeleteText), 0); // HACK to workaround gtk+ nonsense.
icculus@45
   543
icculus@45
   544
//    g_signal_connect(window, "map-event", G_CALLBACK(mappedWindow), NULL);
icculus@45
   545
icculus@45
   546
icculus@45
   547
    GtkVBox *vbox = GTK_VBOX(gtk_vbox_new(FALSE, 0));
icculus@45
   548
    gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(vbox));
icculus@45
   549
icculus@45
   550
    gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(search), FALSE, FALSE, 0);
icculus@45
   551
    gtk_widget_show(GTK_WIDGET(search));
icculus@45
   552
icculus@45
   553
    GtkWidget *vsep = GTK_WIDGET(gtk_vseparator_new());
icculus@45
   554
    gtk_box_pack_start(GTK_BOX(vbox), vsep, FALSE, FALSE, 0);
icculus@45
   555
    gtk_widget_show(vsep);
icculus@45
   556
icculus@45
   557
    return retvalPointer(L, vbox);
icculus@45
   558
} // guiCreateTopLevelMenu
icculus@45
   559
icculus@45
   560
static void clickedMenuItem(GtkButton *button, gpointer arg)
icculus@45
   561
{
icculus@45
   562
    lua_rawgeti(luaState, LUA_REGISTRYINDEX, (int) ((size_t)arg));
icculus@45
   563
    lua_pushlightuserdata(luaState, button);
icculus@45
   564
    lua_call(luaState, 1, 0);
icculus@11
   565
} // clickedMenuItem
icculus@11
   566
icculus@45
   567
static int guiAddMenuItem(lua_State *L)
icculus@12
   568
{
icculus@45
   569
    GtkWidget *vbox = (GtkWidget *) lua_touserdata(L, 1);
icculus@45
   570
    const char *label = luaL_checkstring(L, 2);
icculus@46
   571
    const int checked = lua_toboolean(L, 3);
icculus@46
   572
    const int callback = makeLuaCallback(L, 4);
icculus@46
   573
icculus@46
   574
    if (checked)
icculus@46
   575
    {
icculus@46
   576
        // !!! FIXME: this is pretty lousy.
icculus@46
   577
        const size_t len = strlen(label) + 5;
icculus@46
   578
        char *buf = (char *) alloca(len);
icculus@46
   579
        snprintf(buf, len, "[X] %s", label);
icculus@46
   580
        label = buf;
icculus@46
   581
    } // if
icculus@46
   582
icculus@45
   583
    GtkWidget *item = GTK_WIDGET(gtk_button_new_with_label(label));
icculus@45
   584
    g_signal_connect(item, "key-press-event", G_CALLBACK(checkForEscapeKey), NULL);
icculus@45
   585
    g_signal_connect(item, "clicked", G_CALLBACK(clickedMenuItem), (gpointer) ((size_t)callback));
icculus@45
   586
    g_signal_connect(item, "destroy", G_CALLBACK(destroyCallback), (gpointer) ((size_t)callback));
icculus@11
   587
icculus@45
   588
    //gtk_button_set_image(button, gtk_image_new_from_stock(GTK_STOCK_OPEN, GTK_ICON_SIZE_MENU));
icculus@45
   589
    gtk_button_set_alignment (GTK_BUTTON(item), 0.0f, 0.5f);
icculus@45
   590
    gtk_button_set_relief(GTK_BUTTON(item), GTK_RELIEF_NONE);
icculus@45
   591
    gtk_box_pack_start(GTK_BOX(vbox), item, FALSE, FALSE, 0);
icculus@45
   592
    gtk_widget_show(item);
icculus@45
   593
    return retvalPointer(L, item);
icculus@45
   594
} // guiAddMenuItem
icculus@45
   595
icculus@45
   596
static int guiRemoveAllMenuItems(lua_State *L)
icculus@11
   597
{
icculus@45
   598
    GtkWidget *vbox = (GtkWidget *) lua_touserdata(L, 1);
icculus@45
   599
    GList *children = gtk_container_get_children(GTK_CONTAINER(vbox));
icculus@45
   600
    GList *iter;
icculus@11
   601
icculus@45
   602
    gtk_widget_hide(vbox);
icculus@45
   603
    for (iter = children; iter != NULL; iter = g_list_next(iter))
icculus@11
   604
    {
icculus@45
   605
        if (G_OBJECT_TYPE(iter->data) == GTK_TYPE_BUTTON)
icculus@45
   606
             gtk_widget_destroy(GTK_WIDGET(iter->data));
icculus@45
   607
    } // for
icculus@45
   608
    g_list_free(children);
icculus@11
   609
icculus@45
   610
    return 0;
icculus@45
   611
} // guiRemoveAllMenuItems
icculus@11
   612
icculus@45
   613
static int guiDestroyMenu(lua_State *L)
icculus@45
   614
{
icculus@45
   615
    GtkWidget *widget = (GtkWidget *) lua_touserdata(L, 1);
icculus@45
   616
    gtk_widget_destroy(gtk_widget_get_toplevel(widget));
icculus@45
   617
    return 0;
icculus@45
   618
} // guiDestroyMenu
icculus@11
   619
icculus@45
   620
static int guiShowWindow(lua_State *L)
icculus@11
   621
{
icculus@45
   622
    GtkWidget *widget = (GtkWidget *) lua_touserdata(L, 1);
icculus@45
   623
    //gtk_container_resize_children(GTK_CONTAINER(vbox));
icculus@45
   624
    gtk_widget_show(widget);
icculus@45
   625
    GtkWidget *toplevel = gtk_widget_get_toplevel(widget);
icculus@45
   626
    gtk_window_present(GTK_WINDOW(toplevel));
icculus@11
   627
    return 0;
icculus@45
   628
} // guiShowWindow
icculus@11
   629
icculus@45
   630
static int guiCreateSubMenu(lua_State *L)
icculus@45
   631
{
icculus@45
   632
    GtkWidget *widget = (GtkWidget *) lua_touserdata(L, 1);
icculus@45
   633
    GtkWidget *topwindow = gtk_widget_get_toplevel(widget);
icculus@45
   634
    GtkWindow *window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
icculus@45
   635
    gtk_window_set_keep_above(window, TRUE);
icculus@45
   636
    gtk_window_set_skip_taskbar_hint(window, TRUE);
icculus@45
   637
    gtk_window_set_skip_pager_hint(window, TRUE);
icculus@45
   638
    //g_signal_connect(window, "destroy", G_CALLBACK(destroySubMenu), topwindow);
icculus@45
   639
    gtk_window_set_decorated(window, FALSE);
icculus@11
   640
icculus@45
   641
    GtkVBox *vbox = GTK_VBOX(gtk_vbox_new(FALSE, 0));
icculus@45
   642
    gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(vbox));
icculus@45
   643
icculus@45
   644
    // line the new submenu up...
icculus@45
   645
    //  !!! FIXME: if overflow off right end of screen, go to the left of (widget) instead.
icculus@45
   646
    gint basex, basey, x, y;
icculus@45
   647
    gtk_window_get_position(GTK_WINDOW(topwindow), &basex, &basey);
icculus@45
   648
    gtk_widget_translate_coordinates(widget, topwindow, 0, 0, &x, &y);
icculus@45
   649
    x += basex;
icculus@45
   650
    y += basey;
icculus@45
   651
    x += widget->allocation.width;
icculus@45
   652
    gtk_window_move(window, x, y);
icculus@45
   653
icculus@45
   654
    return retvalPointer(L, vbox);
icculus@45
   655
} // guiCreateSubMenu
icculus@11
   656
icculus@11
   657
icculus@28
   658
static int setPowermateLED_Lua(lua_State *L)
icculus@28
   659
{
icculus@28
   660
    const int enable = lua_toboolean(L, 1);
icculus@28
   661
    setPowermateLED(enable);
icculus@28
   662
    return 0;
icculus@28
   663
} // setPowermateLED_Lua
icculus@28
   664
icculus@28
   665
icculus@17
   666
static void keyhookPressed(void)
icculus@17
   667
{
icculus@17
   668
    lua_getglobal(luaState, "keyhookPressed");
icculus@17
   669
    lua_call(luaState, 0, 0);
icculus@17
   670
} // keyhookPressed
icculus@17
   671
icculus@17
   672
icculus@29
   673
static int pumpLua(void)
icculus@29
   674
{
icculus@29
   675
    lua_getglobal(luaState, "pumpLua");
icculus@29
   676
    lua_call(luaState, 0, 0);
icculus@29
   677
} // pumpLua
icculus@29
   678
icculus@29
   679
icculus@17
   680
static gboolean keyhookPumper(void *arg)
icculus@17
   681
{
icculus@29
   682
    pumpLua();
icculus@17
   683
    if (pumpKeyHook())
icculus@17
   684
        keyhookPressed();
icculus@28
   685
    else if (pumpPowermate())
icculus@28
   686
        keyhookPressed();
icculus@28
   687
icculus@17
   688
    return TRUE;  // keep firing timer
icculus@17
   689
} // keyhookPumper
icculus@17
   690
icculus@17
   691
icculus@11
   692
static int giveControlToGui(lua_State *L)
icculus@11
   693
{
icculus@17
   694
    if (initKeyHook())
icculus@17
   695
    {
icculus@17
   696
        atexit(deinitKeyHook);
icculus@17
   697
        g_timeout_add(200, (GSourceFunc) keyhookPumper, (gpointer) NULL);
icculus@17
   698
    } // if
icculus@17
   699
icculus@11
   700
    gtk_main();
icculus@11
   701
    return 0;
icculus@11
   702
} // giveControlToGui
icculus@11
   703
icculus@7
   704
icculus@46
   705
static int getMountedDisks(lua_State *L)
icculus@46
   706
{
icculus@46
   707
    lua_newtable(L);
icculus@46
   708
    int luai = 1;
icculus@46
   709
icculus@46
   710
    FILE *mounts = setmntent("/etc/mtab", "r");
icculus@46
   711
    if (mounts != NULL)
icculus@46
   712
    {
icculus@46
   713
        struct mntent *ent = NULL;
icculus@46
   714
        while ((ent = getmntent(mounts)) != NULL)
icculus@46
   715
        {
icculus@46
   716
            lua_pushinteger(luaState, luai);
icculus@46
   717
            lua_pushstring(luaState, ent->mnt_dir);
icculus@46
   718
            lua_settable(luaState, -3);
icculus@46
   719
            luai++;
icculus@46
   720
        } // while
icculus@46
   721
        endmntent(mounts);
icculus@46
   722
    } // if
icculus@46
   723
icculus@46
   724
    return 1;  // return the table.
icculus@46
   725
} // getMountedDisks
icculus@46
   726
icculus@46
   727
icculus@0
   728
static void *luaAlloc(void *ud, void *ptr, size_t osize, size_t nsize)
icculus@0
   729
{
icculus@0
   730
    if (nsize == 0)
icculus@0
   731
    {
icculus@0
   732
        free(ptr);
icculus@0
   733
        return NULL;
icculus@0
   734
    } // if
icculus@0
   735
    return realloc(ptr, nsize);
icculus@0
   736
} // luaAlloc
icculus@0
   737
icculus@0
   738
icculus@0
   739
static inline void luaSetCFunc(lua_State *L, lua_CFunction f, const char *sym)
icculus@0
   740
{
icculus@0
   741
    lua_pushcfunction(L, f);
icculus@0
   742
    lua_setglobal(luaState, sym);
icculus@0
   743
} // luaSetCFunc
icculus@0
   744
icculus@0
   745
icculus@0
   746
static int luaFatal(lua_State *L)
icculus@0
   747
{
icculus@0
   748
    const char *errstr = lua_tostring(L, -1);
icculus@0
   749
    fprintf(stderr, "Lua panic: %s\n", errstr ? errstr : "(?)");
icculus@0
   750
    fflush(stderr);
icculus@0
   751
    exit(1);
icculus@0
   752
} // luaFatal
icculus@0
   753
icculus@0
   754
icculus@11
   755
static void deinitLua(void)
icculus@11
   756
{
icculus@11
   757
    if (luaState != NULL)
icculus@11
   758
    {
icculus@11
   759
        lua_close(luaState);
icculus@11
   760
        luaState = NULL;
icculus@11
   761
    } // if
icculus@11
   762
} // deinitLua
icculus@11
   763
icculus@11
   764
icculus@7
   765
static int initLua(const int argc, char **argv)
icculus@0
   766
{
icculus@11
   767
    atexit(deinitLua);
icculus@11
   768
icculus@0
   769
    assert(luaState == NULL);
icculus@0
   770
    luaState = lua_newstate(luaAlloc, NULL);
icculus@0
   771
icculus@0
   772
    lua_atpanic(luaState, luaFatal);
icculus@0
   773
    assert(lua_checkstack(luaState, 20));  // Just in case.
icculus@2
   774
    luaL_openlibs(luaState);
icculus@0
   775
icculus@0
   776
    // Set up initial C functions, etc we want to expose to Lua code...
icculus@0
   777
    luaSetCFunc(luaState, decryptUsingPBKDF2, "decryptUsingPBKDF2");
icculus@0
   778
    luaSetCFunc(luaState, decryptBase64UsingKey, "decryptBase64UsingKey");
icculus@56
   779
    luaSetCFunc(luaState, decryptTopt, "decryptTopt");
icculus@11
   780
    luaSetCFunc(luaState, giveControlToGui, "giveControlToGui");
icculus@12
   781
    luaSetCFunc(luaState, runGuiPasswordPrompt, "runGuiPasswordPrompt");
icculus@12
   782
    luaSetCFunc(luaState, copyToClipboard, "copyToClipboard");
icculus@28
   783
    luaSetCFunc(luaState, setPowermateLED_Lua, "setPowermateLED");
icculus@46
   784
    luaSetCFunc(luaState, calcSha256_Lua, "calcSha256");
icculus@46
   785
    luaSetCFunc(luaState, getMountedDisks, "getMountedDisks");
icculus@0
   786
icculus@45
   787
    luaSetCFunc(luaState, guiCreateTopLevelMenu, "guiCreateTopLevelMenu");
icculus@45
   788
    luaSetCFunc(luaState, guiCreateSubMenu, "guiCreateSubMenu");
icculus@45
   789
    luaSetCFunc(luaState, guiAddMenuItem, "guiAddMenuItem");
icculus@45
   790
    luaSetCFunc(luaState, guiRemoveAllMenuItems, "guiRemoveAllMenuItems");
icculus@45
   791
    luaSetCFunc(luaState, guiDestroyMenu, "guiDestroyMenu");
icculus@45
   792
    luaSetCFunc(luaState, guiShowWindow, "guiShowWindow");
icculus@45
   793
icculus@7
   794
    // Set up argv table...
icculus@7
   795
    lua_newtable(luaState);
icculus@7
   796
    int i;
icculus@28
   797
    int luai = 1;
icculus@7
   798
    for (i = 0; i < argc; i++)
icculus@7
   799
    {
icculus@28
   800
        if (argv[i])
icculus@28
   801
        {
icculus@28
   802
            lua_pushinteger(luaState, luai);
icculus@28
   803
            lua_pushstring(luaState, argv[i]);
icculus@28
   804
            lua_settable(luaState, -3);
icculus@28
   805
            luai++;
icculus@28
   806
        } // if
icculus@7
   807
    } // for
icculus@7
   808
    lua_setglobal(luaState, "argv");
icculus@7
   809
icculus@3
   810
    // Transfer control to Lua...
icculus@0
   811
    if (luaL_dofile(luaState, "1pass.lua") != 0)
icculus@16
   812
        luaFatal(luaState);
icculus@0
   813
icculus@0
   814
    return 1;
icculus@0
   815
} // initLua
icculus@0
   816
icculus@0
   817
icculus@33
   818
static void deinitAll()
icculus@33
   819
{
icculus@33
   820
    deinitPowermate();
icculus@33
   821
    deinitKeyHook();
icculus@33
   822
    deinitLua();
icculus@33
   823
} // deinitAll
icculus@33
   824
icculus@33
   825
icculus@33
   826
static void killerSignalCatcher(int sig)
icculus@33
   827
{
icculus@33
   828
    static int been_run = 0;
icculus@33
   829
    if (been_run)
icculus@33
   830
    {
icculus@33
   831
        fprintf(stderr, "Caught signal %d, terminating HARD.\n", sig);
icculus@33
   832
        _exit(0);
icculus@33
   833
    } // if
icculus@33
   834
icculus@33
   835
    been_run = 1;
icculus@33
   836
    fprintf(stderr, "Caught signal %d, terminating.\n", sig);
icculus@33
   837
    exit(0);  // trigger atexit handlers.
icculus@33
   838
} // killerSignalCatcher
icculus@33
   839
icculus@33
   840
icculus@33
   841
static void initSignals(void)
icculus@33
   842
{
icculus@33
   843
    signal(SIGINT, killerSignalCatcher);
icculus@33
   844
    signal(SIGQUIT, killerSignalCatcher);
icculus@33
   845
    signal(SIGILL, killerSignalCatcher);
icculus@33
   846
    signal(SIGFPE, killerSignalCatcher);
icculus@33
   847
    signal(SIGTERM, killerSignalCatcher);
icculus@33
   848
    signal(SIGPIPE, killerSignalCatcher);
icculus@33
   849
    signal(SIGSEGV, killerSignalCatcher);
icculus@33
   850
    signal(SIGABRT, killerSignalCatcher);
icculus@33
   851
    signal(SIGHUP, killerSignalCatcher);
icculus@33
   852
} // initSignals
icculus@33
   853
icculus@33
   854
icculus@39
   855
// this was from PhysicsFS ( https://icculus.org/physfs/ ), zlib license.
icculus@39
   856
static char *readSymLink(const char *path)
icculus@39
   857
{
icculus@39
   858
    ssize_t len = 64;
icculus@39
   859
    ssize_t rc = -1;
icculus@39
   860
    char *retval = NULL;
icculus@39
   861
icculus@39
   862
    while (1)
icculus@39
   863
    {
icculus@39
   864
         char *ptr = (char *) realloc(retval, (size_t) len);
icculus@39
   865
         if (ptr == NULL)
icculus@39
   866
             break;   // out of memory.
icculus@39
   867
         retval = ptr;
icculus@39
   868
icculus@39
   869
         rc = readlink(path, retval, len);
icculus@39
   870
         if (rc == -1)
icculus@39
   871
             break;  // not a symlink, i/o error, etc.
icculus@39
   872
icculus@39
   873
         else if (rc < len)
icculus@39
   874
         {
icculus@39
   875
             retval[rc] = '\0';  // readlink doesn't null-terminate.
icculus@39
   876
             return retval;  // we're good to go.
icculus@39
   877
         } // else if
icculus@39
   878
icculus@39
   879
         len *= 2;  // grow buffer, try again.
icculus@39
   880
    } // while
icculus@39
   881
icculus@39
   882
    if (retval != NULL)
icculus@39
   883
        free(retval);
icculus@39
   884
    return NULL;
icculus@39
   885
} // readSymLink
icculus@39
   886
icculus@39
   887
icculus@39
   888
static void chdirToApp(void)
icculus@39
   889
{
icculus@39
   890
    char *ptr;
icculus@39
   891
    char *path = readSymLink("/proc/self/exe");
icculus@39
   892
    
icculus@39
   893
    if (path == NULL)
icculus@39
   894
        return;  // maybe we're already there, fail later if not.
icculus@39
   895
icculus@39
   896
    ptr = strrchr(path, '/');
icculus@39
   897
    if (ptr != NULL)
icculus@39
   898
    {
icculus@39
   899
        *ptr = '\0';
icculus@39
   900
        if (chdir(path) == -1)
icculus@39
   901
            fprintf(stderr, "Couldn't chdir() to app directory?! %s\n", strerror(errno));
icculus@39
   902
    } // if
icculus@39
   903
icculus@39
   904
    free(path);
icculus@39
   905
} // chdirToApp
icculus@39
   906
icculus@39
   907
icculus@0
   908
int main(int argc, char **argv)
icculus@0
   909
{
icculus@39
   910
    chdirToApp();
icculus@33
   911
    initSignals();
icculus@28
   912
    initPowermate(&argc, argv);
icculus@11
   913
    gtk_init(&argc, &argv);
icculus@0
   914
icculus@7
   915
    if (!initLua(argc, argv))  // this will move control to 1pass.lua
icculus@0
   916
        return 1;
icculus@0
   917
icculus@0
   918
    return 0;
icculus@0
   919
} // main
icculus@0
   920
icculus@0
   921
// end of 1pass.c ...
icculus@0
   922