X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/9a30e26b7801d63e4ccfe8d36169299c09b89dff..f9520103d3b3a1a36fdb30a46aa4f6c1cd3f1c9e:/unix/uxmisc.c diff --git a/unix/uxmisc.c b/unix/uxmisc.c index 39ef7edd..473aa94e 100644 --- a/unix/uxmisc.c +++ b/unix/uxmisc.c @@ -2,41 +2,282 @@ * PuTTY miscellaneous Unix stuff */ +#include #include +#include +#include +#include +#include +#include #include +#include +#include #include "putty.h" unsigned long getticks(void) { - struct timeval tv; - gettimeofday(&tv, NULL); /* - * This will wrap around approximately every 4000 seconds, i.e. - * just over an hour, which is more than enough. + * We want to use milliseconds rather than the microseconds or + * nanoseconds given by the underlying clock functions, because we + * need a decent number of them to fit into a 32-bit word so it + * can be used for keepalives. */ - return tv.tv_sec * 1000000 + tv.tv_usec; +#if defined HAVE_CLOCK_GETTIME && defined HAVE_DECL_CLOCK_MONOTONIC + { + /* Use CLOCK_MONOTONIC if available, so as to be unconfused if + * the system clock changes. */ + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) + return ts.tv_sec * TICKSPERSEC + + ts.tv_nsec / (1000000000 / TICKSPERSEC); + } +#endif + { + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec * TICKSPERSEC + tv.tv_usec / (1000000 / TICKSPERSEC); + } } -Filename filename_from_str(char *str) +Filename *filename_from_str(const char *str) { - Filename ret; - strncpy(ret.path, str, sizeof(ret.path)); - ret.path[sizeof(ret.path)-1] = '\0'; + Filename *ret = snew(Filename); + ret->path = dupstr(str); return ret; } -char *filename_to_str(Filename fn) +Filename *filename_copy(const Filename *fn) { - return fn.path; + return filename_from_str(fn->path); } -int filename_equal(Filename f1, Filename f2) +const char *filename_to_str(const Filename *fn) { - return !strcmp(f1.path, f2.path); + return fn->path; } -int filename_is_null(Filename fn) +int filename_equal(const Filename *f1, const Filename *f2) { - return !*fn.path; + return !strcmp(f1->path, f2->path); +} + +int filename_is_null(const Filename *fn) +{ + return !fn->path[0]; +} + +void filename_free(Filename *fn) +{ + sfree(fn->path); + sfree(fn); +} + +int filename_serialise(const Filename *f, void *vdata) +{ + char *data = (char *)vdata; + int len = strlen(f->path) + 1; /* include trailing NUL */ + if (data) { + strcpy(data, f->path); + } + return len; +} +Filename *filename_deserialise(void *vdata, int maxsize, int *used) +{ + char *data = (char *)vdata; + char *end; + end = memchr(data, '\0', maxsize); + if (!end) + return NULL; + end++; + *used = end - data; + return filename_from_str(data); +} + +#ifdef DEBUG +static FILE *debug_fp = NULL; + +void dputs(char *buf) +{ + if (!debug_fp) { + debug_fp = fopen("debug.log", "w"); + } + + write(1, buf, strlen(buf)); + + fputs(buf, debug_fp); + fflush(debug_fp); +} +#endif + +char *get_username(void) +{ + struct passwd *p; + uid_t uid = getuid(); + char *user, *ret = NULL; + + /* + * First, find who we think we are using getlogin. If this + * agrees with our uid, we'll go along with it. This should + * allow sharing of uids between several login names whilst + * coping correctly with people who have su'ed. + */ + user = getlogin(); + setpwent(); + if (user) + p = getpwnam(user); + else + p = NULL; + if (p && p->pw_uid == uid) { + /* + * The result of getlogin() really does correspond to + * our uid. Fine. + */ + ret = user; + } else { + /* + * If that didn't work, for whatever reason, we'll do + * the simpler version: look up our uid in the password + * file and map it straight to a name. + */ + p = getpwuid(uid); + if (!p) + return NULL; + ret = p->pw_name; + } + endpwent(); + + return dupstr(ret); +} + +/* + * Display the fingerprints of the PGP Master Keys to the user. + * (This is here rather than in uxcons because it's appropriate even for + * Unix GUI apps.) + */ +void pgp_fingerprints(void) +{ + fputs("These are the fingerprints of the PuTTY PGP Master Keys. They can\n" + "be used to establish a trust path from this executable to another\n" + "one. See the manual for more information.\n" + "(Note: these fingerprints have nothing to do with SSH!)\n" + "\n" + "PuTTY Master Key (RSA), 1024-bit:\n" + " " PGP_RSA_MASTER_KEY_FP "\n" + "PuTTY Master Key (DSA), 1024-bit:\n" + " " PGP_DSA_MASTER_KEY_FP "\n", stdout); +} + +/* + * Set and clear fcntl options on a file descriptor. We don't + * realistically expect any of these operations to fail (the most + * plausible error condition is EBADF, but we always believe ourselves + * to be passing a valid fd so even that's an assertion-fail sort of + * response), so we don't make any effort to return sensible error + * codes to the caller - we just log to standard error and die + * unceremoniously. However, nonblock and no_nonblock do return the + * previous state of O_NONBLOCK. + */ +void cloexec(int fd) { + int fdflags; + + fdflags = fcntl(fd, F_GETFD); + if (fdflags < 0) { + fprintf(stderr, "%d: fcntl(F_GETFD): %s\n", fd, strerror(errno)); + exit(1); + } + if (fcntl(fd, F_SETFD, fdflags | FD_CLOEXEC) < 0) { + fprintf(stderr, "%d: fcntl(F_SETFD): %s\n", fd, strerror(errno)); + exit(1); + } +} +void noncloexec(int fd) { + int fdflags; + + fdflags = fcntl(fd, F_GETFD); + if (fdflags < 0) { + fprintf(stderr, "%d: fcntl(F_GETFD): %s\n", fd, strerror(errno)); + exit(1); + } + if (fcntl(fd, F_SETFD, fdflags & ~FD_CLOEXEC) < 0) { + fprintf(stderr, "%d: fcntl(F_SETFD): %s\n", fd, strerror(errno)); + exit(1); + } +} +int nonblock(int fd) { + int fdflags; + + fdflags = fcntl(fd, F_GETFL); + if (fdflags < 0) { + fprintf(stderr, "%d: fcntl(F_GETFL): %s\n", fd, strerror(errno)); + exit(1); + } + if (fcntl(fd, F_SETFL, fdflags | O_NONBLOCK) < 0) { + fprintf(stderr, "%d: fcntl(F_SETFL): %s\n", fd, strerror(errno)); + exit(1); + } + + return fdflags & O_NONBLOCK; +} +int no_nonblock(int fd) { + int fdflags; + + fdflags = fcntl(fd, F_GETFL); + if (fdflags < 0) { + fprintf(stderr, "%d: fcntl(F_GETFL): %s\n", fd, strerror(errno)); + exit(1); + } + if (fcntl(fd, F_SETFL, fdflags & ~O_NONBLOCK) < 0) { + fprintf(stderr, "%d: fcntl(F_SETFL): %s\n", fd, strerror(errno)); + exit(1); + } + + return fdflags & O_NONBLOCK; +} + +FILE *f_open(const Filename *filename, char const *mode, int is_private) +{ + if (!is_private) { + return fopen(filename->path, mode); + } else { + int fd; + assert(mode[0] == 'w'); /* is_private is meaningless for read, + and tricky for append */ + fd = open(filename->path, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (fd < 0) + return NULL; + return fdopen(fd, mode); + } +} + +FontSpec *fontspec_new(const char *name) +{ + FontSpec *f = snew(FontSpec); + f->name = dupstr(name); + return f; +} +FontSpec *fontspec_copy(const FontSpec *f) +{ + return fontspec_new(f->name); +} +void fontspec_free(FontSpec *f) +{ + sfree(f->name); + sfree(f); +} +int fontspec_serialise(FontSpec *f, void *data) +{ + int len = strlen(f->name); + if (data) + strcpy(data, f->name); + return len + 1; /* include trailing NUL */ +} +FontSpec *fontspec_deserialise(void *vdata, int maxsize, int *used) +{ + char *data = (char *)vdata; + char *end = memchr(data, '\0', maxsize); + if (!end) + return NULL; + *used = end - data + 1; + return fontspec_new(data); }