Convey locale-related environment variables as backup, and nicer error.

Fixes #74.
This commit is contained in:
Keith Winstein
2012-04-14 16:39:07 -04:00
parent 597756b846
commit 8f60f7ab05
10 changed files with 114 additions and 15 deletions
+6
View File
@@ -76,6 +76,12 @@ UDP port number to bind
.B \-c \fICOLORS\fP .B \-c \fICOLORS\fP
Number of colors to advertise to applications through TERM (e.g. 8, 256) Number of colors to advertise to applications through TERM (e.g. 8, 256)
.TP
.B \-l \fINAME=VALUE\fP
Locale-related environment variable to try as part of a fallback
environment, if the startup environment does not specify a character
set of UTF-8.
.SH EXAMPLE .SH EXAMPLE
.nf .nf
+12
View File
@@ -64,6 +64,18 @@ pass between client and server. By default, \fBmosh\fP uses the ports
between 60000 and 61000, but allows the user to request a particular between 60000 and 61000, but allows the user to request a particular
UDP port instead. UDP port instead.
\fBmosh\fP will do its best to arrange a UTF-8 character set locale on
the client and server. The client must have locale-related environment
variables that specify UTF-8. \fBmosh\fP will pass these client
variables to the \fBmosh-server\fP on its command line, but in most
cases they will not need to be used. \fBmosh-server\fP first attempts
to use its own locale-related environment variables, which come from
the system default configuration (sometimes /etc/default/locale) or
from having been passed over the SSH connection. But if these
variables don't call for the use of UTF-8, \fBmosh-server\fP will
apply the locale-related environment variables from the client and try
again.
.SH OPTIONS .SH OPTIONS
.TP .TP
.B \fIcommand\fP .B \fIcommand\fP
+18
View File
@@ -196,6 +196,10 @@ if ( $pid == 0 ) { # child
push @server, ( '-p', $port_request ); push @server, ( '-p', $port_request );
} }
for ( &locale_vars ) {
push @server, ( '-l', $_ );
}
if ( scalar @command > 0 ) { if ( scalar @command > 0 ) {
push @server, '--', @command; push @server, '--', @command;
} }
@@ -237,3 +241,17 @@ if ( $pid == 0 ) { # child
} }
sub shell_quote { join ' ', map {(my $a = $_) =~ s/'/'\\''/g; "'$a'"} @_ } sub shell_quote { join ' ', map {(my $a = $_) =~ s/'/'\\''/g; "'$a'"} @_ }
sub locale_vars {
my @names = qw[LANG LANGUAGE LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION LC_ALL];
my @assignments;
for ( @names ) {
if ( defined $ENV{ $_ } ) {
push @assignments, $_ . q{=} . $ENV{ $_ };
}
}
return @assignments;
}
+1 -1
View File
@@ -61,7 +61,7 @@ int main( void )
/* Adopt native locale */ /* Adopt native locale */
set_native_locale(); set_native_locale();
assert_utf8_locale(); assert( is_utf8_locale() );
for ( int i = 0; i < ITERATIONS; i++ ) { for ( int i = 0; i < ITERATIONS; i++ ) {
/* type a character */ /* type a character */
+1 -1
View File
@@ -57,7 +57,7 @@ int main( int argc __attribute__((unused)),
struct termios saved_termios, raw_termios, child_termios; struct termios saved_termios, raw_termios, child_termios;
set_native_locale(); set_native_locale();
assert_utf8_locale(); assert( is_utf8_locale() );
if ( tcgetattr( STDIN_FILENO, &saved_termios ) < 0 ) { if ( tcgetattr( STDIN_FILENO, &saved_termios ) < 0 ) {
perror( "tcgetattr" ); perror( "tcgetattr" );
+1 -1
View File
@@ -62,7 +62,7 @@ int main( void )
struct termios saved_termios, raw_termios, child_termios; struct termios saved_termios, raw_termios, child_termios;
set_native_locale(); set_native_locale();
assert_utf8_locale(); assert( is_utf8_locale() );
if ( tcgetattr( STDIN_FILENO, &saved_termios ) < 0 ) { if ( tcgetattr( STDIN_FILENO, &saved_termios ) < 0 ) {
perror( "tcgetattr" ); perror( "tcgetattr" );
+28 -3
View File
@@ -72,7 +72,7 @@ using namespace std;
void print_usage( const char *argv0 ) void print_usage( const char *argv0 )
{ {
fprintf( stderr, "Usage: %s new [-s] [-v] [-i LOCALADDR] [-p PORT] [-c COLORS] [-- COMMAND...]\n", argv0 ); fprintf( stderr, "Usage: %s new [-s] [-v] [-i LOCALADDR] [-p PORT] [-c COLORS] [-l NAME=VALUE] [-- COMMAND...]\n", argv0 );
} }
/* Simple spinloop */ /* Simple spinloop */
@@ -113,6 +113,7 @@ int main( int argc, char *argv[] )
int colors = 0; int colors = 0;
bool verbose = false; /* don't close stdin/stdout/stderr */ bool verbose = false; /* don't close stdin/stdout/stderr */
/* Will cause mosh-server not to correctly detach on old versions of sshd. */ /* Will cause mosh-server not to correctly detach on old versions of sshd. */
list<string> locale_vars;
/* strip off command */ /* strip off command */
for ( int i = 0; i < argc; i++ ) { for ( int i = 0; i < argc; i++ ) {
@@ -130,7 +131,7 @@ int main( int argc, char *argv[] )
&& (strcmp( argv[ 1 ], "new" ) == 0) ) { && (strcmp( argv[ 1 ], "new" ) == 0) ) {
/* new option syntax */ /* new option syntax */
int opt; int opt;
while ( (opt = getopt( argc - 1, argv + 1, "i:p:c:sv" )) != -1 ) { while ( (opt = getopt( argc - 1, argv + 1, "i:p:c:svl:" )) != -1 ) {
switch ( opt ) { switch ( opt ) {
case 'i': case 'i':
desired_ip = optarg; desired_ip = optarg;
@@ -148,6 +149,9 @@ int main( int argc, char *argv[] )
case 'v': case 'v':
verbose = true; verbose = true;
break; break;
case 'l':
locale_vars.push_back( string( optarg ) );
break;
default: default:
print_usage( argv[ 0 ] ); print_usage( argv[ 0 ] );
/* don't die on unknown options */ /* don't die on unknown options */
@@ -203,7 +207,28 @@ int main( int argc, char *argv[] )
/* Adopt implementation locale */ /* Adopt implementation locale */
set_native_locale(); set_native_locale();
assert_utf8_locale(); if ( !is_utf8_locale() ) {
/* apply locale-related environment variables from client */
clear_locale_variables();
for ( list<string>::const_iterator i = locale_vars.begin();
i != locale_vars.end();
i++ ) {
char *env_string = strdup( i->c_str() );
assert( env_string );
if ( 0 != putenv( env_string ) ) {
perror( "putenv" );
}
}
/* check again */
set_native_locale();
if ( !is_utf8_locale() ) {
fprintf( stderr, "mosh-server needs a UTF-8 native locale to run.\n\n" );
fprintf( stderr, "Unfortunately, the locale environment variables currently specify\nthe character set \"%s\".\n\n", locale_charset() );
int unused __attribute((unused)) = system( "locale" );
exit( 1 );
}
}
try { try {
return run_server( desired_ip, desired_port, command, colors, verbose ); return run_server( desired_ip, desired_port, command, colors, verbose );
+6 -1
View File
@@ -49,7 +49,12 @@
void STMClient::init( void ) void STMClient::init( void )
{ {
assert_utf8_locale(); if ( !is_utf8_locale() ) {
fprintf( stderr, "mosh-client needs a UTF-8 native locale to run.\n\n" );
fprintf( stderr, "Unfortunately, the locale environment variables currently specify\nthe character set \"%s\".\n\n", locale_charset() );
int unused __attribute((unused)) = system( "locale" );
exit( 1 );
}
/* Verify terminal configuration */ /* Verify terminal configuration */
if ( tcgetattr( STDIN_FILENO, &saved_termios ) < 0 ) { if ( tcgetattr( STDIN_FILENO, &saved_termios ) < 0 ) {
+38 -7
View File
@@ -29,19 +29,50 @@
#include "locale_utils.h" #include "locale_utils.h"
void assert_utf8_locale( void ) { const char *locale_charset( void )
/* Verify locale calls for UTF-8 */ {
if ( strcmp( nl_langinfo( CODESET ), "UTF-8" ) != 0 && static const char ASCII_name[] = "US-ASCII (ANSI_X3.4-1968)";
strcmp( nl_langinfo( CODESET ), "utf-8" ) != 0 ) {
fprintf( stderr, "mosh requires a UTF-8 locale.\n" ); /* Produce more pleasant name of US-ASCII */
exit( 1 ); const char *ret = nl_langinfo( CODESET );
if ( strcmp( ret, "ANSI_X3.4-1968" ) == 0 ) {
ret = ASCII_name;
} }
return ret;
}
bool is_utf8_locale( void ) {
/* Verify locale calls for UTF-8 */
if ( strcmp( locale_charset(), "UTF-8" ) != 0 &&
strcmp( locale_charset(), "utf-8" ) != 0 ) {
return 0;
}
return 1;
} }
void set_native_locale( void ) { void set_native_locale( void ) {
/* Adopt native locale */ /* Adopt native locale */
if ( NULL == setlocale( LC_ALL, "" ) ) { if ( NULL == setlocale( LC_ALL, "" ) ) {
perror( "setlocale" ); perror( "setlocale" );
exit( 1 );
} }
} }
void clear_locale_variables( void ) {
unsetenv( "LANG" );
unsetenv( "LANGUAGE" );
unsetenv( "LC_CTYPE" );
unsetenv( "LC_NUMERIC" );
unsetenv( "LC_TIME" );
unsetenv( "LC_COLLATE" );
unsetenv( "LC_MONETARY" );
unsetenv( "LC_MESSAGES" );
unsetenv( "LC_PAPER" );
unsetenv( "LC_NAME" );
unsetenv( "LC_ADDRESS" );
unsetenv( "LC_TELEPHONE" );
unsetenv( "LC_MEASUREMENT" );
unsetenv( "LC_IDENTIFICATION" );
unsetenv( "LC_ALL" );
}
+3 -1
View File
@@ -19,7 +19,9 @@
#ifndef LOCALE_UTILS_HPP #ifndef LOCALE_UTILS_HPP
#define LOCALE_UTILS_HPP #define LOCALE_UTILS_HPP
void assert_utf8_locale( void ); const char *locale_charset( void );
bool is_utf8_locale( void );
void set_native_locale( void ); void set_native_locale( void );
void clear_locale_variables( void );
#endif #endif