On Tue, Feb 5, 2019 at 6:35 PM Waldek Kozaczuk <[email protected]> wrote:
> > Let me recap couple of things at least for my own sake of having better > understanding of the problem. So there are two aspects around resolv.conf - > (1) how the file is used and (2) how it constructed. As far as 1) goes > normally apps rely on libc to resolve host names which hides existence of > the file. In OSv case which uses musl you can see details here - > https://wiki.musl-libc.org/functional-differences-from-glibc.html (see > 'Names Resolver/DNS section'). Golang (and probably Erlang) are different > and they directly use resolve.conf - I think this golang source code has > details of the logic - > https://github.com/golang/go/blob/9ff11b026089791c4d2bc14c17647f3cb4f4aa22/src/net/conf.go#L100 > . > > I have also found article of how resolv.conf is constructed - > https://www.ctrl.blog/entry/resolvconf-tutorial. > > So here is what I propose: > 1. Make simple change to golang module to add the default resolv.conf (see > https://github.com/cloudius-systems/osv-apps/blob/master/erlang/default/resolv.conf > for example) which points to Google public DNS server. This would make all > Golang apps work out of the box as far as making outgoing HTTP calls which > we discover does not work unless resolv.conf is present. This fix is super > trivial. > 2. For efficiency reasons to make Golang apps use DNS server that is > resolved as part of DHCP lookup. I agree with Brian's approach that this > should be optional. > I think the easiest and perhaps best approach would be to have the DHCP client write a file /etc/resolv.conf when it discovers the DNS server? This is what happens in my Linux system - the /etc/resolv.conf on my laptop has: # Generated by NetworkManager nameserver 192.168.0.1 To support the case where the /etc filesystem is read-only, we can do the following: In the read-only filesystem, put in /etc/resolv.conf a symbolic link to /tmp/resolv.conf, assuming we always mount a writable /tmp. Then, when dhcp.cc will write to "/etc/resolv.conf" (overwritten, e.g., open() with O_TRUNC), it will actually write to that file on /tmp and succeed. > I wonder what others think? > > Waldek > > PS. Please see my patch comments below. > > On Friday, January 25, 2019 at 11:27:37 AM UTC-5, Brian Ledbetter wrote: >> >> Here's my first stab at solving this problem in a backwards-compatible >> way, let me know what you think. I've added a --resolv flag to the loader >> to enable this behavior. >> >> Please see > https://github.com/cloudius-systems/osv/wiki/Formatting-and-sending-patches > for how to format and send patches to the group. Thanks. > >> patch-dhcp.cc: >> >> --- ./core/dhcp.cc~ 2019-01-25 11:18:29.173860626 -0500 >> +++ ./core/dhcp.cc 2019-01-25 11:17:19.546083373 -0500 >> > I agree that dhcp.cc is the right place for this logic. > >> @@ -29,6 +29,12 @@ >> #include <libc/network/__dns.hh> >> >> using namespace boost::asio; >> +bool generate_resolv_conf = false; >> + >> +void enable_resolv_conf() >> +{ >> + generate_resolv_conf = true; >> +} >> >> dhcp::dhcp_worker net_dhcp_worker; >> >> @@ -720,6 +726,31 @@ >> }); >> >> osv::set_dns_config(dm.get_dns_ips(), >> std::vector<std::string>()); >> + >> + // Some linux applications (go, erlang) depend on >> /etc/resolv.conf to be >> + // populated with DNS server information in order to >> function. The --resolv >> + // option has been added to the kernel loader, which will >> enable this >> + // feature. >> + // >> + // TODO: It's possible that the /etc directory does not exist >> at this time. >> + // We should ideally check and create it if necessary. What's >> the right >> + // way to do that? >> > Probably. What if the filesystem is read-only\? > >> + // >> + // TODO: We are currently ignoring DHCP-provided default >> domain >> + // names. I'll have to dig further into the code to see if I >> can ingest >> + // those. >> + // >> + if( generate_resolv_conf ) >> + { >> + FILE *f = fopen("/etc/resolv.conf", "w+"); >> > Should we be appending if file already exists rather than overwrite it? > >> + if( f ) { >> + fprintf( f, "## Autoconfigured by DHCP\n" ); >> > I think this fprintf is redundant - you are printing the result below > anyway. > >> + for( auto &ip : dm.get_dns_ips() ) >> + { >> + fprintf( f, "server %s\n", >> ip.to_string().c_str() ); >> + dhcp_i( "Added DNS server: %s\n", >> ip.to_string().c_str() ); >> + } >> + fclose( f ); >> + dhcp_i( "Generated /etc/resolv.conf" ); >> > You could refine this message that it was generated from DHCP. > >> + } >> > Should we be handling the error of opening file and warn user that we > could not generate the file? > >> + } >> + >> if (dm.get_hostname().size()) { >> sethostname(dm.get_hostname().c_str(), >> dm.get_hostname().size()); >> dhcp_i("Set hostname to: %s", dm.get_hostname().c_str()); >> >> >> patch-loader.cc: >> >> --- ./loader.cc~ 2019-01-25 11:10:40.091359718 -0500 >> +++ ./loader.cc 2019-01-25 11:11:40.243167695 -0500 >> @@ -144,10 +144,13 @@ >> bool opt_assign_net = false; >> bool opt_maxnic = false; >> int maxnic; >> +static bool opt_resolv_conf = false; >> >> static int sampler_frequency; >> static bool opt_enable_sampler = false; >> >> +void enable_resolv_conf(); >> + >> void parse_options(int loader_argc, char** loader_argv) >> { >> namespace bpo = boost::program_options; >> @@ -182,6 +185,7 @@ >> ("delay", bpo::value<float>()->default_value(0), "delay in >> seconds before boot") >> ("redirect", bpo::value<std::string>(), "redirect stdout and >> stderr to file") >> ("disable_rofs_cache", "disable ROFS memory cache") >> + ("resolv", "generate an /etc/resolv.conf file with DHCP DNS >> values (for Linux compatibility)") >> ; >> bpo::variables_map vars; >> // don't allow --foo bar (require --foo=bar) so we can find the >> first non-option >> @@ -234,6 +238,11 @@ >> enable_verbose(); >> } >> >> + if (vars.count("resolv")) { >> + opt_resolv_conf = true; >> + enable_resolv_conf(); >> + } >> + >> if (vars.count("sampler")) { >> sampler_frequency = vars["sampler"].as<int>(); >> opt_enable_sampler = true; >> >> >> patch-run.py: >> >> --- ./scripts/run.py~ 2019-01-25 11:14:20.858654652 -0500 >> +++ ./scripts/run.py 2019-01-25 11:14:52.406553829 -0500 >> @@ -43,6 +43,8 @@ >> execute = cmdline.read() >> if options.verbose: >> execute = "--verbose " + execute >> + if options.resolv: >> + execute = "--resolv " + execute >> > Maybe resolve_DNS? > >> >> if options.jvm_debug or options.jvm_suspend: >> if '-agentlib:jdwp' in execute: >> @@ -461,6 +463,8 @@ >> help="Enable graphics mode.") >> parser.add_argument("-V", "--verbose", action="store_true", >> help="pass --verbose to OSv, to display more >> debugging information on the console") >> + parser.add_argument("--resolv", action="store_true", >> + help="pass --resolv to OSv, to generate an >> /etc/resolv.conf file for Linux compatibility") >> parser.add_argument("--forward", metavar="RULE", action="append", >> default=[], >> help="add network forwarding RULE (QEMU syntax)") >> parser.add_argument("--dry-run", action="store_true", >> >> >> -- > You received this message because you are subscribed to the Google Groups > "OSv Development" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to [email protected]. > For more options, visit https://groups.google.com/d/optout. > -- You received this message because you are subscribed to the Google Groups "OSv Development" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. For more options, visit https://groups.google.com/d/optout.
