{"thread":{"id":"29107","subject":"[PATCHv2 0/7] getpass refactoring","startedAt":"2011-12-08T08:21:19Z","lastAt":"2011-12-09T23:58:06Z","messageCount":11,"participants":["Jeff King","Jakub Narebski","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"180560","messageId":"20111208082118.GA1507@sigill.intra.peff.net","threadId":"29107","inReplyTo":null,"subject":"[PATCHv2 0/7] getpass refactoring","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-12-08T08:21:19Z","receivedAt":"2011-12-08T08:21:19Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"This is a re-roll of the earlier \"echo usernames as they are typed\"\nseries here:\n\n  http://article.gmane.org/gmane.comp.version-control.git/185970\n\nIt turns out that getpass() isn't even really good at getting passwords.\nSee patch 5/7 for details. This series replaces it entirely if you set\nHAVE_DEV_TTY (otherwise, we continue to fallback to getpass()).\n\n  [1/7]: imap-send: avoid buffer overflow\n  [2/7]: imap-send: don't check return value of git_getpass\n  [3/7]: move git_getpass to its own source file\n  [4/7]: refactor git_getpass into generic prompt function\n  [5/7]: add generic terminal prompt function\n  [6/7]: prompt: use git_terminal_prompt\n  [7/7]: credential: use git_prompt instead of git_getpass\n\nIt should be applied on top of the jk/credentials topic.\n\n-Peff\n"},{"id":"180561","messageId":"20111208082317.GA26409@sigill.intra.peff.net","threadId":"29107","inReplyTo":"20111208082118.GA1507@sigill.intra.peff.net","subject":"[PATCH 1/7] imap-send: avoid buffer overflow","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-12-08T08:23:17Z","receivedAt":"2011-12-08T08:23:17Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"We format the password prompt in an 80-character static\nbuffer. It contains the remote host and username, so it's\nunlikely to overflow (or be exploitable by a remote\nattacker), but there's no reason not to be careful and use\na strbuf.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\nJust something I noticed while doing the cleanup in the next patch.\n\n imap-send.c |    7 ++++---\n 1 files changed, 4 insertions(+), 3 deletions(-)\n\ndiff --git a/imap-send.c b/imap-send.c\nindex e1ad1a4..4c1e897 100644\n--- a/imap-send.c\n+++ b/imap-send.c\n@@ -1209,9 +1209,10 @@ static int auth_cram_md5(struct imap_store *ctx, struct imap_cmd *cmd, const cha\n \t\t\tgoto bail;\n \t\t}\n \t\tif (!srvc->pass) {\n-\t\t\tchar prompt[80];\n-\t\t\tsprintf(prompt, \"Password (%s@%s): \", srvc->user, srvc->host);\n-\t\t\targ = git_getpass(prompt);\n+\t\t\tstruct strbuf prompt = STRBUF_INIT;\n+\t\t\tstrbuf_addf(&prompt, \"Password (%s@%s): \", srvc->user, srvc->host);\n+\t\t\targ = git_getpass(prompt.buf);\n+\t\t\tstrbuf_release(&prompt);\n \t\t\tif (!arg) {\n \t\t\t\tperror(\"getpass\");\n \t\t\t\texit(1);\n-- \n1.7.8.rc2.8.gf0f4f\n"},{"id":"180562","messageId":"20111208082427.GB26409@sigill.intra.peff.net","threadId":"29107","inReplyTo":"20111208082118.GA1507@sigill.intra.peff.net","subject":"[PATCH 2/7] imap-send: don't check return value of git_getpass","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-12-08T08:24:27Z","receivedAt":"2011-12-08T08:24:27Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"git_getpass will always die() if we weren't able to get\ninput, so there's no point looking for NULL.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\nNot a bug, but just useless code I noticed.\n\n imap-send.c |    4 ----\n 1 files changed, 0 insertions(+), 4 deletions(-)\n\ndiff --git a/imap-send.c b/imap-send.c\nindex 4c1e897..227253e 100644\n--- a/imap-send.c\n+++ b/imap-send.c\n@@ -1213,10 +1213,6 @@ static int auth_cram_md5(struct imap_store *ctx, struct imap_cmd *cmd, const cha\n \t\t\tstrbuf_addf(&prompt, \"Password (%s@%s): \", srvc->user, srvc->host);\n \t\t\targ = git_getpass(prompt.buf);\n \t\t\tstrbuf_release(&prompt);\n-\t\t\tif (!arg) {\n-\t\t\t\tperror(\"getpass\");\n-\t\t\t\texit(1);\n-\t\t\t}\n \t\t\tif (!*arg) {\n \t\t\t\tfprintf(stderr, \"Skipping account %s@%s, no password\\n\", srvc->user, srvc->host);\n \t\t\t\tgoto bail;\n-- \n1.7.8.rc2.8.gf0f4f\n"},{"id":"180563","messageId":"20111208082443.GC26409@sigill.intra.peff.net","threadId":"29107","inReplyTo":"20111208082118.GA1507@sigill.intra.peff.net","subject":"[PATCH 3/7] move git_getpass to its own source file","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-12-08T08:24:43Z","receivedAt":"2011-12-08T08:24:43Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"This is currently in connect.c, but really has nothing to\ndo with the git protocol itself. Let's make a new source\nfile all about prompting the user, which will make it\ncleaner to refactor.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\nSame as patch 1 from the previous series.\n\n Makefile     |    2 ++\n cache.h      |    1 -\n connect.c    |   44 --------------------------------------------\n credential.c |    1 +\n imap-send.c  |    1 +\n prompt.c     |   48 ++++++++++++++++++++++++++++++++++++++++++++++++\n prompt.h     |    6 ++++++\n 7 files changed, 58 insertions(+), 45 deletions(-)\n create mode 100644 prompt.c\n create mode 100644 prompt.h\n\ndiff --git a/Makefile b/Makefile\nindex a26f58a..b024e2f 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -563,6 +563,7 @@ LIB_H += parse-options.h\n LIB_H += patch-ids.h\n LIB_H += pkt-line.h\n LIB_H += progress.h\n+LIB_H += prompt.h\n LIB_H += quote.h\n LIB_H += reflog-walk.h\n LIB_H += refs.h\n@@ -669,6 +670,7 @@ LIB_OBJS += pkt-line.o\n LIB_OBJS += preload-index.o\n LIB_OBJS += pretty.o\n LIB_OBJS += progress.o\n+LIB_OBJS += prompt.o\n LIB_OBJS += quote.o\n LIB_OBJS += reachable.o\n LIB_OBJS += read-cache.o\ndiff --git a/cache.h b/cache.h\nindex 2e6ad36..f320c98 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -1024,7 +1024,6 @@ struct ref {\n extern struct ref *find_ref_by_name(const struct ref *list, const char *name);\n \n #define CONNECT_VERBOSE       (1u << 0)\n-extern char *git_getpass(const char *prompt);\n extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);\n extern int finish_connect(struct child_process *conn);\n extern int git_connection_is_socket(struct child_process *conn);\ndiff --git a/connect.c b/connect.c\nindex 51990fa..519e527 100644\n--- a/connect.c\n+++ b/connect.c\n@@ -619,47 +619,3 @@ int finish_connect(struct child_process *conn)\n \tfree(conn);\n \treturn code;\n }\n-\n-char *git_getpass(const char *prompt)\n-{\n-\tconst char *askpass;\n-\tstruct child_process pass;\n-\tconst char *args[3];\n-\tstatic struct strbuf buffer = STRBUF_INIT;\n-\n-\taskpass = getenv(\"GIT_ASKPASS\");\n-\tif (!askpass)\n-\t\taskpass = askpass_program;\n-\tif (!askpass)\n-\t\taskpass = getenv(\"SSH_ASKPASS\");\n-\tif (!askpass || !(*askpass)) {\n-\t\tchar *result = getpass(prompt);\n-\t\tif (!result)\n-\t\t\tdie_errno(\"Could not read password\");\n-\t\treturn result;\n-\t}\n-\n-\targs[0] = askpass;\n-\targs[1]\t= prompt;\n-\targs[2] = NULL;\n-\n-\tmemset(&pass, 0, sizeof(pass));\n-\tpass.argv = args;\n-\tpass.out = -1;\n-\n-\tif (start_command(&pass))\n-\t\texit(1);\n-\n-\tstrbuf_reset(&buffer);\n-\tif (strbuf_read(&buffer, pass.out, 20) < 0)\n-\t\tdie(\"failed to read password from %s\\n\", askpass);\n-\n-\tclose(pass.out);\n-\n-\tif (finish_command(&pass))\n-\t\texit(1);\n-\n-\tstrbuf_setlen(&buffer, strcspn(buffer.buf, \"\\r\\n\"));\n-\n-\treturn buffer.buf;\n-}\ndiff --git a/credential.c b/credential.c\nindex a17eafe..fbb7231 100644\n--- a/credential.c\n+++ b/credential.c\n@@ -3,6 +3,7 @@\n #include \"string-list.h\"\n #include \"run-command.h\"\n #include \"url.h\"\n+#include \"prompt.h\"\n \n void credential_init(struct credential *c)\n {\ndiff --git a/imap-send.c b/imap-send.c\nindex 227253e..43588e8 100644\n--- a/imap-send.c\n+++ b/imap-send.c\n@@ -25,6 +25,7 @@\n #include \"cache.h\"\n #include \"exec_cmd.h\"\n #include \"run-command.h\"\n+#include \"prompt.h\"\n #ifdef NO_OPENSSL\n typedef void *SSL;\n #else\ndiff --git a/prompt.c b/prompt.c\nnew file mode 100644\nindex 0000000..42a1c9f\n--- /dev/null\n+++ b/prompt.c\n@@ -0,0 +1,48 @@\n+#include \"cache.h\"\n+#include \"run-command.h\"\n+#include \"strbuf.h\"\n+#include \"prompt.h\"\n+\n+char *git_getpass(const char *prompt)\n+{\n+\tconst char *askpass;\n+\tstruct child_process pass;\n+\tconst char *args[3];\n+\tstatic struct strbuf buffer = STRBUF_INIT;\n+\n+\taskpass = getenv(\"GIT_ASKPASS\");\n+\tif (!askpass)\n+\t\taskpass = askpass_program;\n+\tif (!askpass)\n+\t\taskpass = getenv(\"SSH_ASKPASS\");\n+\tif (!askpass || !(*askpass)) {\n+\t\tchar *result = getpass(prompt);\n+\t\tif (!result)\n+\t\t\tdie_errno(\"Could not read password\");\n+\t\treturn result;\n+\t}\n+\n+\targs[0] = askpass;\n+\targs[1]\t= prompt;\n+\targs[2] = NULL;\n+\n+\tmemset(&pass, 0, sizeof(pass));\n+\tpass.argv = args;\n+\tpass.out = -1;\n+\n+\tif (start_command(&pass))\n+\t\texit(1);\n+\n+\tstrbuf_reset(&buffer);\n+\tif (strbuf_read(&buffer, pass.out, 20) < 0)\n+\t\tdie(\"failed to read password from %s\\n\", askpass);\n+\n+\tclose(pass.out);\n+\n+\tif (finish_command(&pass))\n+\t\texit(1);\n+\n+\tstrbuf_setlen(&buffer, strcspn(buffer.buf, \"\\r\\n\"));\n+\n+\treturn buffer.buf;\n+}\ndiff --git a/prompt.h b/prompt.h\nnew file mode 100644\nindex 0000000..0fd7bd9\n--- /dev/null\n+++ b/prompt.h\n@@ -0,0 +1,6 @@\n+#ifndef PROMPT_H\n+#define PROMPT_H\n+\n+char *git_getpass(const char *prompt);\n+\n+#endif /* PROMPT_H */\n-- \n1.7.8.rc2.8.gf0f4f\n"},{"id":"180564","messageId":"20111208083133.GD26409@sigill.intra.peff.net","threadId":"29107","inReplyTo":"20111208082118.GA1507@sigill.intra.peff.net","subject":"[PATCH 4/7] refactor git_getpass into generic prompt function","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-12-08T08:31:33Z","receivedAt":"2011-12-08T08:31:33Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"This will allow callers to specify more options (e.g.,\nleaving echo on). The original git_getpass becomes a slim\nwrapper around the new function.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\nSimilar to patch 2 from the previous series. Two big differences:\n\n 1. The first series accidentally dropped the \"die if we don't get a\n    password\" behavior during the refactor, but we want to keep it.\n\n 2. The first series had a special \"name\" parameter just for generating\n    error messages. This drops it in the name of simplicity, so error\n    messages have gone from (assuming you don't have a tty):\n\n      Could not read password: No such device or address\n\n    to:\n\n      Could not read 'Username for 'https://example.com': ': No such\n      device or address\n\n    which is verbose, yes, but contains a little more useful\n    information. The formatting is rather unfortunate, but I don't think\n    it's worth worrying too much about.\n\n prompt.c |   46 ++++++++++++++++++++++++++++++----------------\n prompt.h |    3 +++\n 2 files changed, 33 insertions(+), 16 deletions(-)\n\ndiff --git a/prompt.c b/prompt.c\nindex 42a1c9f..2002644 100644\n--- a/prompt.c\n+++ b/prompt.c\n@@ -3,26 +3,13 @@\n #include \"strbuf.h\"\n #include \"prompt.h\"\n \n-char *git_getpass(const char *prompt)\n+static char *do_askpass(const char *cmd, const char *prompt)\n {\n-\tconst char *askpass;\n \tstruct child_process pass;\n \tconst char *args[3];\n \tstatic struct strbuf buffer = STRBUF_INIT;\n \n-\taskpass = getenv(\"GIT_ASKPASS\");\n-\tif (!askpass)\n-\t\taskpass = askpass_program;\n-\tif (!askpass)\n-\t\taskpass = getenv(\"SSH_ASKPASS\");\n-\tif (!askpass || !(*askpass)) {\n-\t\tchar *result = getpass(prompt);\n-\t\tif (!result)\n-\t\t\tdie_errno(\"Could not read password\");\n-\t\treturn result;\n-\t}\n-\n-\targs[0] = askpass;\n+\targs[0] = cmd;\n \targs[1]\t= prompt;\n \targs[2] = NULL;\n \n@@ -35,7 +22,7 @@\n \n \tstrbuf_reset(&buffer);\n \tif (strbuf_read(&buffer, pass.out, 20) < 0)\n-\t\tdie(\"failed to read password from %s\\n\", askpass);\n+\t\tdie(\"failed to get '%s' from %s\\n\", prompt, cmd);\n \n \tclose(pass.out);\n \n@@ -46,3 +33,30 @@\n \n \treturn buffer.buf;\n }\n+\n+char *git_prompt(const char *prompt, int flags)\n+{\n+\tchar *r;\n+\n+\tif (flags & PROMPT_ASKPASS) {\n+\t\tconst char *askpass;\n+\n+\t\taskpass = getenv(\"GIT_ASKPASS\");\n+\t\tif (!askpass)\n+\t\t\taskpass = askpass_program;\n+\t\tif (!askpass)\n+\t\t\taskpass = getenv(\"SSH_ASKPASS\");\n+\t\tif (askpass && *askpass)\n+\t\t\treturn do_askpass(askpass, prompt);\n+\t}\n+\n+\tr = getpass(prompt);\n+\tif (!r)\n+\t\tdie_errno(\"could not read '%s'\", prompt);\n+\treturn r;\n+}\n+\n+char *git_getpass(const char *prompt)\n+{\n+\treturn git_prompt(prompt, PROMPT_ASKPASS);\n+}\ndiff --git a/prompt.h b/prompt.h\nindex 0fd7bd9..9ab85a7 100644\n--- a/prompt.h\n+++ b/prompt.h\n@@ -1,6 +1,9 @@\n #ifndef PROMPT_H\n #define PROMPT_H\n \n+#define PROMPT_ASKPASS (1<<0)\n+\n+char *git_prompt(const char *prompt, int flags);\n char *git_getpass(const char *prompt);\n \n #endif /* PROMPT_H */\n-- \n1.7.8.rc2.8.gf0f4f\n"},{"id":"180565","messageId":"20111208083323.GE26409@sigill.intra.peff.net","threadId":"29107","inReplyTo":"20111208082118.GA1507@sigill.intra.peff.net","subject":"[PATCH 5/7] add generic terminal prompt function","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-12-08T08:33:23Z","receivedAt":"2011-12-08T08:33:23Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"When we need to prompt the user for input interactively, we\nwant to access their terminal directly. We can't rely on\nstdio because it may be connected to pipes or files, rather\nthan the terminal. Instead, we use \"getpass()\", because it\nabstracts the idea of prompting and reading from the\nterminal.  However, it has some problems:\n\n  1. It never echoes the typed characters, which makes it OK\n     for passwords but annoying for other input (like usernames).\n\n  2. Some implementations of getpass() have an extremely\n     small input buffer (e.g., Solaris 8 is reported to\n     support only 8 characters).\n\n  3. Some implementations of getpass() will fall back to\n     reading from stdin (e.g., glibc). We explicitly don't\n     want this, because our stdin may be connected to a pipe\n     speaking a particular protocol, and reading will\n     disrupt the protocol flow (e.g., the remote-curl\n     helper).\n\n  4. Some implementations of getpass() turn off signals, so\n     that hitting \"^C\" on the terminal does not break out of\n     the password prompt. This can be a mild annoyance.\n\nInstead, let's provide an abstract \"git_terminal_prompt\"\nfunction that addresses these concerns. This patch includes\nan implementation based on /dev/tty, enabled by setting\nHAVE_DEV_TTY. The fallback is to use getpass() as before.\n\nFor now, only Linux enables this by default. People on other\n/dev/tty-enabled systems can submit patches to turn it on\nonce they have tested it.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\nThis is the interesting one. I suspect most Unixes will want to use\nthis, but I was conservative about enabling it. msysgit can just drop\nits own function into compat/terminal.c, and the existing custom\ngetpass() implementation can just go away.\n\n Makefile          |   10 ++++++\n compat/terminal.c |   81 +++++++++++++++++++++++++++++++++++++++++++++++++++++\n compat/terminal.h |    6 ++++\n 3 files changed, 97 insertions(+), 0 deletions(-)\n create mode 100644 compat/terminal.c\n create mode 100644 compat/terminal.h\n\ndiff --git a/Makefile b/Makefile\nindex b024e2f..2a9bb3d 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -227,6 +227,9 @@ all::\n #\n # Define NO_REGEX if you have no or inferior regex support in your C library.\n #\n+# Define HAVE_DEV_TTY if your system can open /dev/tty to interact with the\n+# user.\n+#\n # Define GETTEXT_POISON if you are debugging the choice of strings marked\n # for translation.  In a GETTEXT_POISON build, you can turn all strings marked\n # for translation into gibberish by setting the GIT_GETTEXT_POISON variable\n@@ -523,6 +526,7 @@ LIB_H += compat/bswap.h\n LIB_H += compat/cygwin.h\n LIB_H += compat/mingw.h\n LIB_H += compat/obstack.h\n+LIB_H += compat/terminal.h\n LIB_H += compat/win32/pthread.h\n LIB_H += compat/win32/syslog.h\n LIB_H += compat/win32/poll.h\n@@ -610,6 +614,7 @@ LIB_OBJS += color.o\n LIB_OBJS += combine-diff.o\n LIB_OBJS += commit.o\n LIB_OBJS += compat/obstack.o\n+LIB_OBJS += compat/terminal.o\n LIB_OBJS += config.o\n LIB_OBJS += connect.o\n LIB_OBJS += connected.o\n@@ -833,6 +838,7 @@ ifeq ($(uname_S),Linux)\n \tNO_STRLCPY = YesPlease\n \tNO_MKSTEMPS = YesPlease\n \tHAVE_PATHS_H = YesPlease\n+\tHAVE_DEV_TTY = YesPlease\n endif\n ifeq ($(uname_S),GNU/kFreeBSD)\n \tNO_STRLCPY = YesPlease\n@@ -1639,6 +1645,10 @@ ifdef HAVE_PATHS_H\n \tBASIC_CFLAGS += -DHAVE_PATHS_H\n endif\n \n+ifdef HAVE_DEV_TTY\n+\tBASIC_CFLAGS += -DHAVE_DEV_TTY\n+endif\n+\n ifdef DIR_HAS_BSD_GROUP_SEMANTICS\n \tCOMPAT_CFLAGS += -DDIR_HAS_BSD_GROUP_SEMANTICS\n endif\ndiff --git a/compat/terminal.c b/compat/terminal.c\nnew file mode 100644\nindex 0000000..49f16ca\n--- /dev/null\n+++ b/compat/terminal.c\n@@ -0,0 +1,81 @@\n+#include \"git-compat-util.h\"\n+#include \"compat/terminal.h\"\n+#include \"sigchain.h\"\n+#include \"strbuf.h\"\n+\n+#ifndef NO_DEV_TTY\n+\n+static int term_fd = -1;\n+static struct termios old_term;\n+\n+static void restore_term(void)\n+{\n+\tif (term_fd < 0)\n+\t\treturn;\n+\n+\ttcsetattr(term_fd, TCSAFLUSH, &old_term);\n+\tterm_fd = -1;\n+}\n+\n+static void restore_term_on_signal(int sig)\n+{\n+\trestore_term();\n+\tsigchain_pop(sig);\n+\traise(sig);\n+}\n+\n+char *git_terminal_prompt(const char *prompt, int echo)\n+{\n+\tstatic struct strbuf buf = STRBUF_INIT;\n+\tint r;\n+\tFILE *fh;\n+\n+\tfh = fopen(\"/dev/tty\", \"w+\");\n+\tif (!fh)\n+\t\treturn NULL;\n+\n+\tif (!echo) {\n+\t\tstruct termios t;\n+\n+\t\tif (tcgetattr(fileno(fh), &t) < 0) {\n+\t\t\tfclose(fh);\n+\t\t\treturn NULL;\n+\t\t}\n+\n+\t\told_term = t;\n+\t\tterm_fd = fileno(fh);\n+\t\tsigchain_push_common(restore_term_on_signal);\n+\n+\t\tt.c_lflag &= ~ECHO;\n+\t\tif (tcsetattr(fileno(fh), TCSAFLUSH, &t) < 0) {\n+\t\t\tterm_fd = -1;\n+\t\t\tfclose(fh);\n+\t\t\treturn NULL;\n+\t\t}\n+\t}\n+\n+\tfputs(prompt, fh);\n+\tfflush(fh);\n+\n+\tr = strbuf_getline(&buf, fh, '\\n');\n+\tif (!echo) {\n+\t\tputc('\\n', fh);\n+\t\tfflush(fh);\n+\t}\n+\n+\trestore_term();\n+\tfclose(fh);\n+\n+\tif (r == EOF)\n+\t\treturn NULL;\n+\treturn buf.buf;\n+}\n+\n+#else\n+\n+char *git_terminal_prompt(const char *prompt, int echo)\n+{\n+\treturn getpass(prompt);\n+}\n+\n+#endif\ndiff --git a/compat/terminal.h b/compat/terminal.h\nnew file mode 100644\nindex 0000000..97db7cd\n--- /dev/null\n+++ b/compat/terminal.h\n@@ -0,0 +1,6 @@\n+#ifndef COMPAT_TERMINAL_H\n+#define COMPAT_TERMINAL_H\n+\n+char *git_terminal_prompt(const char *prompt, int echo);\n+\n+#endif /* COMPAT_TERMINAL_H */\n-- \n1.7.8.rc2.8.gf0f4f\n"},{"id":"180566","messageId":"20111208083339.GF26409@sigill.intra.peff.net","threadId":"29107","inReplyTo":"20111208082118.GA1507@sigill.intra.peff.net","subject":"[PATCH 6/7] prompt: use git_terminal_prompt","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-12-08T08:33:39Z","receivedAt":"2011-12-08T08:33:39Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"Our custom implementation of git_terminal_prompt has many\nadvantages over regular getpass(), as described in the prior\ncommit.\n\nThis also lets us implement a PROMPT_ECHO flag for callers\nwho want it.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n prompt.c |    3 ++-\n prompt.h |    1 +\n 2 files changed, 3 insertions(+), 1 deletions(-)\n\ndiff --git a/prompt.c b/prompt.c\nindex 2002644..72ab9de 100644\n--- a/prompt.c\n+++ b/prompt.c\n@@ -2,6 +2,7 @@\n #include \"run-command.h\"\n #include \"strbuf.h\"\n #include \"prompt.h\"\n+#include \"compat/terminal.h\"\n \n static char *do_askpass(const char *cmd, const char *prompt)\n {\n@@ -50,7 +51,7 @@\n \t\t\treturn do_askpass(askpass, prompt);\n \t}\n \n-\tr = getpass(prompt);\n+\tr = git_terminal_prompt(prompt, flags & PROMPT_ECHO);\n \tif (!r)\n \t\tdie_errno(\"could not read '%s'\", prompt);\n \treturn r;\ndiff --git a/prompt.h b/prompt.h\nindex 9ab85a7..04f321a 100644\n--- a/prompt.h\n+++ b/prompt.h\n@@ -2,6 +2,7 @@\n #define PROMPT_H\n \n #define PROMPT_ASKPASS (1<<0)\n+#define PROMPT_ECHO    (1<<1)\n \n char *git_prompt(const char *prompt, int flags);\n char *git_getpass(const char *prompt);\n-- \n1.7.8.rc2.8.gf0f4f\n"},{"id":"180567","messageId":"20111208083351.GG26409@sigill.intra.peff.net","threadId":"29107","inReplyTo":"20111208082118.GA1507@sigill.intra.peff.net","subject":"[PATCH 7/7] credential: use git_prompt instead of git_getpass","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-12-08T08:33:51Z","receivedAt":"2011-12-08T08:33:51Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"We use git_getpass to retrieve the username and password\nfrom the terminal. However, git_getpass will not echo the\nusername as the user types. We can fix this by using the\nmore generic git_prompt, which underlies git_getpass but\nlets us specify an \"echo\" option.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n credential.c |   15 +++++++--------\n 1 files changed, 7 insertions(+), 8 deletions(-)\n\ndiff --git a/credential.c b/credential.c\nindex fbb7231..62d1c56 100644\n--- a/credential.c\n+++ b/credential.c\n@@ -109,7 +109,8 @@ static void credential_describe(struct credential *c, struct strbuf *out)\n \t\tstrbuf_addf(out, \"/%s\", c->path);\n }\n \n-static char *credential_ask_one(const char *what, struct credential *c)\n+static char *credential_ask_one(const char *what, struct credential *c,\n+\t\t\t\tint flags)\n {\n \tstruct strbuf desc = STRBUF_INIT;\n \tstruct strbuf prompt = STRBUF_INIT;\n@@ -121,11 +122,7 @@ static void credential_describe(struct credential *c, struct strbuf *out)\n \telse\n \t\tstrbuf_addf(&prompt, \"%s: \", what);\n \n-\t/* FIXME: for usernames, we should do something less magical that\n-\t * actually echoes the characters. However, we need to read from\n-\t * /dev/tty and not stdio, which is not portable (but getpass will do\n-\t * it for us). http.c uses the same workaround. */\n-\tr = git_getpass(prompt.buf);\n+\tr = git_prompt(prompt.buf, flags);\n \n \tstrbuf_release(&desc);\n \tstrbuf_release(&prompt);\n@@ -135,9 +132,11 @@ static void credential_describe(struct credential *c, struct strbuf *out)\n static void credential_getpass(struct credential *c)\n {\n \tif (!c->username)\n-\t\tc->username = credential_ask_one(\"Username\", c);\n+\t\tc->username = credential_ask_one(\"Username\", c,\n+\t\t\t\t\t\t PROMPT_ASKPASS|PROMPT_ECHO);\n \tif (!c->password)\n-\t\tc->password = credential_ask_one(\"Password\", c);\n+\t\tc->password = credential_ask_one(\"Password\", c,\n+\t\t\t\t\t\t PROMPT_ASKPASS);\n }\n \n int credential_read(struct credential *c, FILE *fp)\n-- \n1.7.8.rc2.8.gf0f4f\n"},{"id":"180620","messageId":"m38vmmivkc.fsf@localhost.localdomain","threadId":"29107","inReplyTo":"20111208083323.GE26409@sigill.intra.peff.net","subject":"Re: [PATCH 5/7] add generic terminal prompt function","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2011-12-08T21:48:33Z","receivedAt":"2011-12-08T21:48:33Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> diff --git a/Makefile b/Makefile\n> index b024e2f..2a9bb3d 100644\n> --- a/Makefile\n> +++ b/Makefile\n> @@ -227,6 +227,9 @@ all::\n\n> +# Define HAVE_DEV_TTY if your system can open /dev/tty to interact with the\n> +# user.\n\n> @@ -833,6 +838,7 @@ ifeq ($(uname_S),Linux)\n>  \tNO_STRLCPY = YesPlease\n>  \tNO_MKSTEMPS = YesPlease\n>  \tHAVE_PATHS_H = YesPlease\n> +\tHAVE_DEV_TTY = YesPlease\n>  endif\n\nHere you use HAVE_DEV_TTY (by the way, I wonder if it could be\nautomatically detected by ./configure script)...\n\n> diff --git a/compat/terminal.c b/compat/terminal.c\n> new file mode 100644\n> index 0000000..49f16ca\n> --- /dev/null\n> +++ b/compat/terminal.c\n> @@ -0,0 +1,81 @@\n> +#include \"git-compat-util.h\"\n> +#include \"compat/terminal.h\"\n> +#include \"sigchain.h\"\n> +#include \"strbuf.h\"\n> +\n> +#ifndef NO_DEV_TTY\n\n> +#endif\n\n  +#endif /* NO_DEV_TTY */\n\n...and here you have NO_DEV_TTY\n\n-- \nJakub Narębski\n"},{"id":"180621","messageId":"20111208215218.GA12127@sigill.intra.peff.net","threadId":"29107","inReplyTo":"m38vmmivkc.fsf@localhost.localdomain","subject":"Re: [PATCH 5/7] add generic terminal prompt function","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-12-08T21:52:18Z","receivedAt":"2011-12-08T21:52:18Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Dec 08, 2011 at 01:48:33PM -0800, Jakub Narebski wrote:\n\n> > @@ -833,6 +838,7 @@ ifeq ($(uname_S),Linux)\n> >  \tNO_STRLCPY = YesPlease\n> >  \tNO_MKSTEMPS = YesPlease\n> >  \tHAVE_PATHS_H = YesPlease\n> > +\tHAVE_DEV_TTY = YesPlease\n> >  endif\n> \n> Here you use HAVE_DEV_TTY (by the way, I wonder if it could be\n> automatically detected by ./configure script)...\n> [...]\n> ...and here you have NO_DEV_TTY\n\nWhoops. Thanks for catching. I converted it to NO_DEV_TTY, which would\nturn this code on by default (because I think _most_ platforms we use\nare going to want this), but then I decided to go the conservative route\nand let platforms opt into it.\n\nAnd of course since my platform is the one that enables it, I didn't\nnotice during my testing.\n\nI'll fix it for the next re-roll.\n\n-Peff\n"},{"id":"180733","messageId":"7vzkf1b8mp.fsf@alter.siamese.dyndns.org","threadId":"29107","inReplyTo":"20111208083133.GD26409@sigill.intra.peff.net","subject":"Re: [PATCH 4/7] refactor git_getpass into generic prompt function","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-12-09T23:58:06Z","receivedAt":"2011-12-09T23:58:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n>  2. The first series had a special \"name\" parameter just for generating\n>     error messages. This drops it in the name of simplicity, so error\n>     messages have gone from (assuming you don't have a tty):\n>\n>       Could not read password: No such device or address\n>\n>     to:\n>\n>       Could not read 'Username for 'https://example.com': ': No such\n>       device or address\n>\n>     which is verbose, yes, but contains a little more useful\n>     information. The formatting is rather unfortunate,...\n\nIt also would be unpleasant to i18n it, I suspect. \n\n> +\tr = getpass(prompt);\n> +\tif (!r)\n> +\t\tdie_errno(\"could not read '%s'\", prompt);\n\nTaking advantage of the \"prompt-string\"-ness of the message, this might be\na cuter workaround:\n\n    fatal: Password: <<could not be read>>\n\nBut I do not think it matters that much. Let's queue what you have, and\nwork out these details in-tree.\n"}]}