# HG changeset patch # User Jun Wu # Date 1482511020 0 # Node ID 112915e9a363283915a102e0fa7fdbfa9e9bf343 # Parent eb69f78c48abb2766b5945c502cc53050d7679ac chg: let hgc_open support long path "sizeof(sun_path)" is too small. Use the chdir trick to support long socket path, like "mercurial.util.bindunixsocket". It's useful for cases where TMPDIR is long. Modern OS X rewrites TMPDIR to a long value. And we probably want to use XDG_RUNTIME_DIR [2] for Linux. The approach is a bit different from the previous plan, where we will have hgc_openat and pass cmdserveropts.sockdirfd to it. That's because the current change is easier: chg has to pass a full path to "hg" as the "--address" parameter. There is no "--address-basename" or "--address-dirfd" flags. The next patch will remove "sockdirfd". Note: It'd be nice if we can use a native "connectat" implementation. However, that's not available everywhere. Some platform (namely FreeBSD) does support it, but the implementation has bugs so it cannot be used [2]. [1]: https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html [2]: https://www.mercurial-scm.org/pipermail/mercurial-devel/2016-April/082892.html diff -r eb69f78c48ab -r 112915e9a363 contrib/chg/hgclient.c --- a/contrib/chg/hgclient.c Sat Dec 24 15:38:27 2016 -0500 +++ b/contrib/chg/hgclient.c Fri Dec 23 16:37:00 2016 +0000 @@ -425,10 +425,43 @@ struct sockaddr_un addr; addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, sockname, sizeof(addr.sun_path)); + + /* use chdir to workaround small sizeof(sun_path) */ + int bakfd = -1; + const char *basename = sockname; + { + const char *split = strrchr(sockname, '/'); + if (split && split != sockname) { + if (split[1] == '\0') + abortmsg("sockname cannot end with a slash"); + size_t len = split - sockname; + char sockdir[len + 1]; + memcpy(sockdir, sockname, len); + sockdir[len] = '\0'; + + bakfd = open(".", O_DIRECTORY); + if (bakfd == -1) + abortmsgerrno("cannot open cwd"); + + int r = chdir(sockdir); + if (r != 0) + abortmsgerrno("cannot chdir %s", sockdir); + + basename = split + 1; + } + } + if (strlen(basename) >= sizeof(addr.sun_path)) + abortmsg("sockname is too long: %s", basename); + strncpy(addr.sun_path, basename, sizeof(addr.sun_path)); addr.sun_path[sizeof(addr.sun_path) - 1] = '\0'; + /* real connect */ int r = connect(fd, (struct sockaddr *)&addr, sizeof(addr)); + if (bakfd != -1) { + fchdirx(bakfd); + close(bakfd); + } + if (r < 0) { close(fd); if (errno == ENOENT || errno == ECONNREFUSED)