home *** CD-ROM | disk | FTP | other *** search
/ Chip 2007 January, February, March & April / Chip-Cover-CD-2007-02.iso / boot / i386 / root / usr / share / YaST2 / modules / NetworkDevices.ycp < prev    next >
Text File  |  2006-11-29  |  34KB  |  1,333 lines

  1. /**
  2.  * File:    modules/NetworkDevices.ycp
  3.  * Package:    Network configuration
  4.  * Summary:    Interface manipulation (/etc/sysconfig/network/ifcfg-*)
  5.  * Authors:    Michal Svec <msvec@suse.cz>
  6.  *
  7.  * $Id: NetworkDevices.ycp 34371 2006-11-14 10:07:31Z kmachalkova $
  8.  *
  9.  * The new sysconfig naming is interface (eg. eth0) vs. device
  10.  * (eg. NE2000 card), but historically yast has called them device
  11.  * vs. module.
  12.  */
  13.  
  14. {
  15.  
  16. module "NetworkDevices";
  17. textdomain "base";
  18.  
  19. import "Arch";
  20. import "Map";
  21. import "Netmask";
  22. import "String";
  23.  
  24. /**
  25.  * False suppresses tones of logs 'NetworkDevices.ycp:ABC Check(eth,id-00:aa:bb:cc:dd:ee,)'
  26.  */
  27. global boolean report_every_check = true;
  28.  
  29. /**
  30.  * Current device identifier
  31.  * @example eth0, eth1:blah, lo, ...
  32.  * Add, Edit and Delete copy the requested device info (via Select)
  33.  * to Name and Current,
  34.  * Commit puts it back
  35.  */
  36. global string Name = "";
  37.  
  38. // value is not just string, can be a map for aliases 
  39. typedef map<string, any> ifcfg_t;
  40. typedef map<string, map<string, ifcfg_t> > devices_t;
  41.  
  42. /**
  43.  * Current device information
  44.  * @example $["BOOTPROTO":"dhcp", "STARTMODE":"auto"]
  45.  */
  46. global ifcfg_t Current = $[];
  47.  
  48. /**
  49.  * Interface information:
  50.  * Devices[string type, string id] is a map with the contents of
  51.  * ifcfg-<i>type</i>-<i>id</i>. Separating type from id is useful because
  52.  * the type determines the fields of the interface file.
  53.  * Multiple addresses for an interface are nested maps
  54.  * [type, id, "_aliases", aid]
  55.  * @see Read
  56.  */
  57. devices_t Devices = $[];
  58.  
  59. /**
  60.  * Devices information
  61.  * @see Read
  62.  */
  63. devices_t OriginalDevices = $[];
  64.  
  65. /**
  66.  * Deleted devices
  67.  */
  68. list<string> Deleted = [];
  69.  
  70. /**
  71.  * True if devices are already read
  72.  */
  73. boolean initialized = false;
  74.  
  75. /**
  76.  * Which operation is pending?
  77.  */
  78. /* global */ symbol operation = nil;
  79. // FIXME: used in lan/address.ycp (#17346) -> "global"
  80.  
  81. /**
  82.  * Predefined network card regular expressions
  83.  */
  84. global map<string,string> CardRegex = $[
  85.     "netcard"    : "arc|bnep|ci|ctc|dummy|escon|eth|fddi|ficon|hsi|qeth|lcs|iucv|myri|tr|usb|wlan|xp",
  86.     "modem"    : "ppp|modem",
  87.     "isdn"    : "isdn|ippp",
  88.     "dsl"    : "dsl",
  89.     /* other: irlan|lo|plip|... */
  90. ];
  91.  
  92. // define string HotplugRegex(list<string> devs);
  93.  
  94. /**
  95.  * Supported hotplug types
  96.  */
  97. list<string> HotplugTypes = [ "pcmcia", "usb"/*, "pci" */];
  98.  
  99. /**
  100.  * Create a list of hot-pluggable device names for the given devices
  101.  */
  102. define string HotplugRegex(list<string> devs) {
  103.     string ret = "";
  104.     foreach(string dev, devs, {
  105.     foreach(string hot, HotplugTypes, {
  106.         ret = ret + "|" + dev + "-" + hot + "|" + dev + "-" + hot + "-";
  107.     });
  108.     });
  109.     return ret;
  110. }
  111.  
  112. /**
  113.  * Predefined network device regular expressions
  114.  */
  115. global map<string,string> DeviceRegex = $[
  116.     /* device types */
  117.     "netcard"    : CardRegex["netcard"]:"" + HotplugRegex(["eth", "tr", "wlan"]) + "|usb-usb|usb-usb-",
  118.     "modem"    : CardRegex["modem"]:"",
  119.     "isdn"    : CardRegex["isdn"]:"" + HotplugRegex(["isdn", "ippp"]),
  120.     "dsl"    : CardRegex["dsl"]:"",
  121.     /* device groups */
  122.     "dialup"    : CardRegex["modem"]:"" + "|" + CardRegex["dsl"]:"" + "|" + CardRegex["isdn"]:"",
  123. ];
  124.  
  125. /**
  126.  * Types in order from fastest to slowest.
  127.  * @see FastestRegexps
  128.  */
  129. map<integer,string> FastestTypes = $[
  130.     1 : "dsl",
  131.     2 : "isdn",
  132.     3 : "modem",
  133.     4 : "netcard"
  134. ];
  135.  
  136. /**
  137.  * @see Push
  138.  */
  139. map stack = $[];
  140.  
  141. // -------------------- components of configuration names --------------------
  142.  
  143. /**
  144.  * A single character used to separate alias id
  145.  */
  146. string alias_separator = "#";
  147.  
  148. /**
  149.  * ifcfg name = type + id + alias_id
  150.  * If id is numeric, it is not separated from type, otherwise separated by "-"
  151.  * Id may be empty
  152.  * Alias_id, if nonempty, is separated by alias_separator
  153.  */
  154. string ifcfg_name_regex =
  155.     "^" +
  156.     // ip6: #48696
  157.     "(ip6tnl|mip6mnha|[" + String::CAlpha() + "]+)" + "-?" +
  158.     "([^" + alias_separator + "]*)" + alias_separator + "?" +
  159.     "(.*)" +
  160.     "$";
  161.  
  162. string ifcfg_part (string ifcfg, string part) {
  163.     if (regexpmatch (ifcfg, ifcfg_name_regex) != true)
  164.     {
  165.     return "";
  166.     }
  167.     string ret = regexpsub (ifcfg, ifcfg_name_regex, "\\" + part);
  168.     return (ret == nil)? "": ret;
  169. }
  170.  
  171. /**
  172.  * Return a device type
  173.  * @param dev device
  174.  * @return device type
  175.  * @example device_type("eth1") -> "eth"
  176.  * @example device_type("eth-pcmcia-0") -> "eth-pcmcia"
  177.  */
  178. global string device_type (string dev) {
  179.     return ifcfg_part (dev, "1");
  180. }
  181.  
  182. /**
  183.  * Return device type in human readable form :-)
  184.  * @param dev device
  185.  * @return device type 
  186.  * @example GetDeviceType(eth-bus-pci-0000:01:07.0) -> "network card"
  187.  * @example GetDeviceType(modem0) -> "modem"
  188.  */
  189. global string GetDeviceType(string dev) {
  190.     if (regexpmatch(dev,"^" + DeviceRegex["netcard"]:"")) {
  191.     return(_("network card"));
  192.     }
  193.     else if (regexpmatch(dev,"^" + DeviceRegex["modem"]:"")) {
  194.     return(_("modem"));
  195.     }
  196.     else if (regexpmatch(dev,"^" + DeviceRegex["isdn"]:"")) {
  197.     return(_("ISDN"));
  198.     }
  199.     else if (regexpmatch(dev,"^" + DeviceRegex["dsl"]:"")) {
  200.     return(_("DSL"));
  201.     }
  202.     else return(_("unknown"));
  203. }
  204.  
  205. /**
  206.  * Return a device number
  207.  * @param dev device
  208.  * @return device number
  209.  * @example device_num("eth1") -> "1"
  210.  * @example device_num("lo") -> ""
  211.  */
  212. global string device_num (string dev) {
  213.     return ifcfg_part (dev, "2");
  214. }
  215.  
  216. /**
  217.  * Return a device alias number
  218.  * @param dev device
  219.  * @return alias number
  220.  * @example alias_num("eth1#2") -> "2"
  221.  * @example alias_num("eth1#blah") -> "blah"
  222.  */
  223. global string alias_num (string dev) {
  224.     return ifcfg_part (dev, "3");
  225. }
  226.  
  227. /**
  228.  * Create a device name from its type and number
  229.  * @param typ device type
  230.  * @param num device number
  231.  * @return device name
  232.  * @example device_name("eth", "1") -> "eth1"
  233.  * @example device_name("lo", "") -> "lo"
  234.  */
  235. global string device_name (string typ, string num) {
  236.     if(typ == nil || typ == "") {
  237.     y2error("wrong type: %1", typ);
  238.     return nil;
  239.     }
  240.     if(num == nil /* || num < 0 */) {
  241.     y2error("wrong number: %1", num);
  242.     return nil;
  243.     }
  244.     /* FIXME: devname
  245.     if(IsHotplug(typ) && num != "") return sformat("%1-%2", typ, num);
  246.     return sformat("%1%2", typ, num);
  247.     */
  248.     if(regexpmatch(num, "^[0-9]*$"))
  249.     return sformat("%1%2", typ, num);
  250.     return sformat("%1-%2", typ, num);
  251. }
  252.  
  253. /**
  254.  * Create a alias name from its type and numbers
  255.  * @param typ device type
  256.  * @param num device number
  257.  * @param anum alias number
  258.  * @return alias name
  259.  * @example alias_name("eth", "1", "2") -> "eth1#2"
  260.  */
  261. global string alias_name (string typ, string num, string anum) {
  262.     if(typ == nil || typ == "") {
  263.     y2error("wrong type: %1", typ);
  264.     return nil;
  265.     }
  266.     if(num == nil /* || num < 0 */) {
  267.     y2error("wrong number: %1", num);
  268.     return nil;
  269.     }
  270.     if(anum == nil || anum == "") {
  271.     y2error("wrong alias number: %1", anum);
  272.     return nil;
  273.     }
  274.     return sformat("%1#%2", device_name(typ, num), anum);
  275. }
  276.  
  277. /**
  278.  * Test hotplugability of a device
  279.  * @param type device type
  280.  * @return true if hotpluggable
  281.  */
  282. global boolean IsHotplug (string type) {
  283.     if(type == "" || type == nil) return false;
  284.     if(regexpmatch(type, "(pcmcia|usb|pci)$")) return true;
  285.     return false;
  286. }
  287.  
  288. /**
  289.  * Return real type of the device (incl. PCMCIA, USB, ...)
  290.  * @param type basic device type
  291.  * @param hotplug hot plug type
  292.  * @return real type
  293.  * @example RealType("eth", "usb") -> "eth-usb"
  294.  */
  295. global string RealType (string type, string hotplug) {
  296.  
  297.     y2debug("type=%1", type);
  298.     if(type == "" || type == nil) {
  299.     y2error("Wrong type: %1", type);
  300.     return "eth";
  301.     }
  302.  
  303.     if(hotplug == "" || hotplug == nil)
  304.     return type;
  305.  
  306.     string realtype = type + "-" + hotplug;
  307.     y2debug("realtype=%1", realtype);
  308.     return realtype;
  309. }
  310.  
  311. // ---------------------------------------------------------------------------
  312.  
  313. /**
  314.  * STARTMODE: onboot, on and boot are aliases for auto
  315.  */
  316. global map<string, any> CanonicalizeStartmode (map<string, any> ifcfg) {
  317.     map <string, string> canonicalize_startmode = $[
  318.     "on": "auto",
  319.     "boot": "auto",
  320.     "onboot": "auto",
  321.     ];
  322.     string startmode = ifcfg["STARTMODE"]:"";
  323.     ifcfg["STARTMODE"] = canonicalize_startmode[startmode]:startmode;
  324.     return ifcfg;
  325. }
  326.  
  327. /**
  328.  * Canonicalize netmask data (#46885)
  329.  * Sysconfig allows:
  330.  * IPADDR=10.0.0.1/8
  331.  * IPADDR=10.0.0.1 PREFIXLEN=8
  332.  * IPADDR=10.0.0.1 NETMASK=255.0.0.0
  333.  * (IPADDR overrides PREFIXLEN, NETMASK used only if prefix length unspecified)
  334.  * If prefix length and NETMASK are unspecified, 32 is implied.
  335.  * Canonicalize it to
  336.  * IPADDR=10.0.0.1 PREFIXLEN= NETMASK=255.0.0.0
  337.  * @param ifcfg a map containing IPADDR and possibly NETMASK, PREFIXLEN
  338.  * and possibly other fields
  339.  * @return the map with IPADDR, NETMASK adjusted; PREFIXLEN ""
  340.  * others unchanged. If IPADDR is empty, return the original.
  341.  */
  342. global map<string, any> CanonicalizeIP (map<string, any> ifcfg) {
  343.     if (ifcfg == nil)
  344.     {
  345.     return nil;
  346.     }
  347.  
  348.     list<string> ip_and_prefix = splitstring (ifcfg["IPADDR"]:"", "/");
  349.     string ipaddr = ip_and_prefix[0]:"";
  350.     if (ipaddr == "")        // DHCP or inconsistent
  351.     {
  352.     return ifcfg;
  353.     }
  354.     string prefixlen = ip_and_prefix[1]:"";
  355.     if (prefixlen == "")
  356.     {
  357.     prefixlen = ifcfg["PREFIXLEN"]:"";
  358.     }
  359.     if (prefixlen == "")
  360.     {
  361.     prefixlen = tostring (Netmask::ToBits (ifcfg["NETMASK"]:""));
  362.     }
  363.  
  364.     // Now we have ipaddr and prefixlen
  365.     // Let's compute the rest
  366.     string netmask = Netmask::FromBits (tointeger (prefixlen));
  367.     map<string, any> ret = ifcfg;
  368.     ret["IPADDR"] = ipaddr;
  369.     ret["PREFIXLEN"] = "";
  370.     ret["NETMASK"] = netmask;
  371.     return ret;
  372. }
  373.  
  374. list<string> SensitiveFields = [
  375.     "WIRELESS_WPA_PASSWORD",
  376.     "WIRELESS_WPA_PSK",
  377.     // the unnumbered one should be empty but just in case
  378.     "WIRELESS_KEY",
  379.     "WIRELESS_KEY_0",
  380.     "WIRELESS_KEY_1",
  381.     "WIRELESS_KEY_2",
  382.     "WIRELESS_KEY_3",
  383.     ];
  384.  
  385. /**
  386.  * Conceal secret information, such as WEP keys, so that the output
  387.  * can be passed to y2log and bugzilla.
  388.  * @param ifcfg one ifcfg
  389.  * @return ifcfg with secret fields masked out
  390.  */
  391. global map ConcealSecrets1 (map<string, any> ifcfg) {
  392.     if (ifcfg == nil)
  393.     {
  394.     return nil;
  395.     }
  396.     map out = mapmap (string k, any v, ifcfg, {
  397.     if (contains (SensitiveFields, k) && v != "")
  398.     {
  399.         v = "CONCEALED";
  400.     }
  401.     return $[k: v];
  402.     });
  403.     return out;
  404. }
  405.  
  406. /**
  407.  * Conceal secret information, such as WEP keys, so that the output
  408.  * can be passed to y2log and bugzilla. (#65741)
  409.  * @param devs a two-level map of ifcfgs like Devices
  410.  * @return ifcfgs with secret fields masked out
  411.  */
  412. global map ConcealSecrets (map devs) {
  413.     if (devs == nil)
  414.     {
  415.     return nil;
  416.     }
  417.     map out = mapmap (string t, map<string, map<string, any> > tdevs,
  418.               (map<string, map<string, map<string, any> > >) devs, {
  419.     map tout = mapmap (string id, map<string, any> ifcfg, tdevs, {
  420.         return $[id: ConcealSecrets1 (ifcfg)];
  421.     });
  422.     return $[t: tout];
  423.     });
  424.     return out;
  425. }
  426.  
  427. /**
  428.  * Read devices from files
  429.  * @return true if sucess
  430.  */
  431. global define boolean Read() {
  432.  
  433.     // initialized = true; // FIXME
  434.     if(initialized == true) return true;
  435.  
  436.     Devices = $[];
  437.  
  438.     /* Variables which could be suffixed and thus duplicated */
  439.     list Locals = [ "IPADDR", "REMOTE_IPADDR", "NETMASK", "PREFIXLEN",
  440.         "BROADCAST", "SCOPE", "LABEL", "IP_OPTIONS" ];
  441.  
  442.     /* preparation */
  443.     list<string> allfiles = SCR::Dir(.network.section);
  444.     if(allfiles == nil) allfiles = [];
  445.     list<string> devices = filter(string file, allfiles, {
  446.     return !regexpmatch(file, "[~]");
  447.     });
  448.     y2debug("devices=%1", devices);
  449.  
  450.     /* FIXME: devname
  451.     devices = filter(string d, devices, {
  452.     return regexpmatch(d, "[a-z][a-z-]*[0-9]*");
  453.     });
  454.     y2debug("devices=%1", devices);
  455.     */
  456.  
  457.     /* Read devices */
  458.     maplist(string d, devices, {
  459.     string devtype = device_type(d);
  460.  
  461.     string devnum = "";
  462.     // if(regexpmatch(d, "[a-z][a-z-]*[0-9]+"))
  463.         devnum = sformat("%1", device_num(d));
  464.     y2debug("devnum=%1", devnum);
  465.  
  466.     map<string, ifcfg_t> dev = Devices[devtype]:$[];
  467.     if(haskey(dev, devnum)) {
  468.         y2error("device already present: %1", devnum);
  469.         return;
  470.     }
  471.  
  472.     string pth = ".network.value.\"" + device_name(devtype, devnum) + "\"";
  473.     y2debug("pth=%1", pth);
  474.     list<string> values = SCR::Dir(topath(pth));
  475.     y2debug("values=%1", values);
  476.  
  477.     map<string, any> config = $[];
  478.     maplist(string val, values, {
  479.         string item = (string) SCR::Read(topath(pth + "." + val));
  480.         y2debug("item=%1", item);
  481.         if(item == nil) return;
  482.         /* No underscore '_' -> global */
  483.         /* Also temporarily standard globals */
  484.         if(find(val, "_") < 0 || contains(Locals, val)) {
  485.         config[val] = item;
  486.         return;
  487.         }
  488.         /* Try to strip _suffix */
  489.         string v = substring(val, 0, findlastof(val, "_"));
  490.         string s = substring(val, findlastof(val, "_"));
  491.         if(size(s) > 1) s = substring(s, 1);
  492.         y2milestone("%1:%2:%3", val, v, s);
  493.         /* Global */
  494.         if(!contains(Locals, v))
  495.         config[val] = item;
  496.         /* Local */
  497.         else {
  498.         map _aliases = config["_aliases"]:$[];
  499.         map suf = _aliases[s]:$[];
  500.         suf[v] = item;
  501.         _aliases[s] = suf;
  502.         config["_aliases"] = _aliases;
  503.         }
  504.     });
  505.     y2milestone("config=%1", ConcealSecrets1 (config));
  506.  
  507.     // canonicalize, #46885
  508.     map <string, map> caliases = mapmap (string a, map<string, any> c, (map<string,map<string, any> >)config["_aliases"]:$[], {
  509.         return $[a: CanonicalizeIP (c)];
  510.     });
  511.     if (caliases != $[])    // unconditionally?
  512.     {
  513.         config["_aliases"] = caliases;
  514.     }
  515.     config = CanonicalizeIP (config);
  516.     config = CanonicalizeStartmode (config);
  517.  
  518.     dev[devnum] = config;
  519.     Devices[devtype] = dev;
  520.     });
  521.     y2debug("Devices=%1", Devices);
  522.  
  523.     OriginalDevices = Devices;
  524.     initialized = true;
  525.     return true;
  526. }
  527.  
  528. /**
  529.  */
  530. define map<string,map> Filter(map<string,map> devices, string devregex) {
  531.     if(devices == nil || devregex == nil || devregex == "")
  532.     return devices;
  533.  
  534.     string regex = "^(" + DeviceRegex[devregex]:devregex + ")[0-9]*$";
  535.     y2debug("regex=%1", regex);
  536.     devices = filter(string file, map devmap, devices, {
  537.     return regexpmatch(file, regex) == true;
  538.     });
  539.     y2debug("devices=%1", devices);
  540.     return devices;
  541. }
  542.  
  543. /**
  544.  * Used in BuildSummary, BuildOverview
  545.  */
  546. global map<string,map> FilterDevices (string devregex) {
  547.     return Filter (Devices, devregex);
  548. }
  549.  
  550. /**
  551.  */
  552. define map<string,map> FilterNOT(map<string,map> devices, string devregex) {
  553.     if(devices == nil || devregex == nil || devregex == "")
  554.     return $[];
  555.  
  556.     string regex = "^(" + DeviceRegex[devregex]:devregex + ")[0-9]*$";
  557.     y2debug("regex=%1", regex);
  558.     devices = filter(string file, map devmap, devices, {
  559.     return regexpmatch(file, regex) != true;
  560.     });
  561.     y2debug("devices=%1", devices);
  562.     return devices;
  563. }
  564.  
  565. /**
  566.  * For the NAME field, filter out characters that will case problems
  567.  * for the shell or the ini agent. (#72164)
  568.  * It should be done in more places but this field is most susceptible.
  569.  * @param s a string
  570.  * @return s with some characters removed, esp. the single quote
  571.  */
  572. string ShellSafe (string s) {
  573.     s = filterchars (s, String::CGraph () + " ");
  574.     return deletechars (s, "'");
  575. }
  576.  
  577. /**
  578.  * SCR::Write (p, ShellSafe (s)) and if s had to be changed,
  579.  * log the _path_ (not the value, for privacy).
  580.  * @see ShellSafe
  581.  * @param p SCR path
  582.  * @param s value
  583.  * @return success
  584.  */
  585. boolean ShellSafeWrite (path p, string s) {
  586.     string safe_s = ShellSafe (s);
  587.     if (safe_s != s)
  588.     {
  589.     y2milestone ("Changed: %1", p);
  590.     }
  591.     return SCR::Write (p, safe_s);
  592. }
  593.  
  594. /**
  595.  * Write devices to files
  596.  * @param devregex regular expression for the device type
  597.  * @return true if success
  598.  * @example NetworkDevice::Write("eth|tr");
  599.  */
  600. global define boolean Write(string devregex) {
  601.  
  602.     y2milestone("Writing configuration");
  603.     y2debug("Devices=%1", Devices);
  604.     y2debug("Deleted=%1", Deleted);
  605.  
  606.     map Devs = Filter(Devices, devregex);
  607.     map OriginalDevs = Filter(OriginalDevices, devregex);
  608.     y2milestone("OriginalDevs=%1", ConcealSecrets (OriginalDevs));
  609.     y2milestone("Devs=%1", ConcealSecrets (Devs));
  610.  
  611.     /* Check for changes */
  612.     if(Devs == OriginalDevs) {
  613.     y2milestone("No changes to %1 devices -> nothing to write", devregex);
  614.     return true;
  615.     }
  616.  
  617.     /* remove deleted devices */
  618.     y2milestone("Deleted=%1", Deleted);
  619.     foreach(string d, Deleted, {
  620.     // if(!haskey(OriginalDevs, d)) return;
  621.     string anum = alias_num (d);
  622.     if (anum == "")
  623.     {
  624.         /* delete config file */
  625.         path p = add (.network.section, d);
  626.         y2debug("deleting: %1", p);
  627.         SCR::Write(p, nil);
  628.     }
  629.     else
  630.     {
  631.         string typ = device_type (d);
  632.         string num = device_num (d);
  633.         string dev = device_name (typ, num);
  634.         path base = add (.network.value, dev);
  635.         // look in OriginalDevs because we need to catch all variables
  636.         // of the alias
  637.         foreach (string key, any dummy, OriginalDevs[typ, num, "_aliases", anum]:$[], {
  638.         path p = add (base, key + "_" + anum);
  639.         y2debug ("deleting: %1", p);
  640.         SCR::Write (p, nil);
  641.         });
  642.     }
  643.     });
  644.  
  645.     /* Devices with chmod=0600 */
  646.     list<string> chmod = [];
  647.  
  648.     /* write all devices */
  649.     maplist(string typ, map<string,map<string,any> > devsmap, (map<string, map<string, map<string, any> > >) Devs, {
  650.     maplist(string num, map<string,any> devmap, devsmap, {
  651.         /* write sysconfig */
  652.         string dev = device_name(typ, num);
  653.         string p = ".network.value.\"" + dev + "\".";
  654.  
  655.         /* write all keys to config */
  656.         maplist(string k, (list<string>) Map::Keys(devmap), {
  657.         /* Write aliases */
  658.         if(k == "_aliases") {
  659.             maplist(string anum, map<string,string> amap, devmap[k]:$[], {
  660.             // Normally defaulting the label would be done
  661.             // when creating the map, not here when
  662.             // writing, but we create it in 2 ways so it's
  663.             // better here. Actually it does not work because
  664.             // the edit dialog nukes LABEL :-(
  665.             boolean seen_label = false;
  666.  
  667.             maplist(string ak, string av, amap, {
  668.                 string akk = ak + "_" + anum;
  669.                 ShellSafeWrite (topath (p + akk), av);
  670.                 seen_label = seen_label || ak == "LABEL";
  671.             });
  672.  
  673.             if (!seen_label)
  674.             {
  675.                 ShellSafeWrite (topath (p + ("LABEL_" + anum)), anum);
  676.             }
  677.             });
  678.         }
  679.         /* Write regular keys */
  680.         else
  681.             ShellSafeWrite (topath (p + k), devmap[k]:"");
  682.         });
  683.  
  684.         /* update libhd unique number * /
  685.         // FIXME: move it somewhere else: hardware
  686.         string unq = devmap["UNIQUE"]:"";
  687.         if(unq != "") SCR::Write(.probe.status.configured, unq, `yes);
  688.         */
  689.  
  690.         /* 0600 if contains encryption key (#24842) */
  691.         boolean has_key = find (string k, SensitiveFields,
  692.                     ``( devmap[k]:"" != "" )) != nil;
  693.         string file = "/etc/sysconfig/network/ifcfg-" + dev;
  694.         y2debug("Permission change: %1, %2", has_key, file);
  695.         if(has_key) {
  696.         y2debug("CHANGED");
  697.         chmod = add(chmod, file);
  698.         }
  699.     });
  700.     });
  701.  
  702.     /* Finish him */
  703.     SCR::Write(.network, nil);
  704.  
  705.     /* CHMOD */
  706.     y2debug("chmod=%1", chmod);
  707.     maplist(string file, chmod, {
  708.     y2debug("changing: %1", file);
  709.     SCR::Execute(.target.bash, "/bin/chmod 0600 " + file);
  710.     });
  711.  
  712.     // Deleted = [];
  713.     // OriginalDevices = Devices;
  714.     // Cannot do it because we have written only part of Devices.
  715.     // This module should be rewritten to objects.
  716.     return true;
  717. }
  718.  
  719. /**
  720.  * Import data
  721.  * @param settings settings to be imported
  722.  * @return true on success
  723.  */
  724. global define boolean Import(string devregex, map<string,map> devices) {
  725.     map Devs = FilterNOT(Devices, devregex);
  726.     y2debug("Devs=%1", Devs);
  727.  
  728.     devices = mapmap(string typ, map devsmap, devices, {
  729.     return $[typ: mapmap(string num, map<string, any> config, (map<string,map<string, any> >) devsmap, {
  730.         config = CanonicalizeIP (config);
  731.         config = CanonicalizeStartmode (config);
  732.         return $[num: config];
  733.     })];
  734.     });
  735.  
  736.     Devices = (devices_t) union(Devs, devices);
  737.     OriginalDevices = nil;
  738.     return true;
  739. }
  740.  
  741. /**
  742.  * Export data
  743.  * @return dumped settings (later acceptable by Import())
  744.  */
  745. global define map<string,map> Export(string devregex) {
  746.     map Devs = Filter(Devices, devregex);
  747.     y2debug("Devs=%1", Devs);
  748.     return (map<string,map>) Devs;
  749. }
  750.  
  751. /**
  752.  * Were the devices changed?
  753.  * @return true if modified
  754.  */
  755. global define boolean Modified(string devregex) {
  756.     map Devs = Filter(Devices, devregex);
  757.     map OriginalDevs = Filter(OriginalDevices, devregex);
  758.     y2debug("OriginalDevs=%1", OriginalDevs);
  759.     y2debug("Devs=%1", Devs);
  760.     return Devs == OriginalDevs;
  761. }
  762.  
  763. global define list<string> GetFreeDevices(string type, integer num) {
  764.     y2debug("Devices=%1", Devices);
  765.     y2debug("type,num=%1,%2", type, num);
  766.     y2debug("Devices[%1]=%2", type, Devices[type]:$[]);
  767.  
  768.     list curdevs = Map::Keys(Devices[type]:$[]);
  769.     y2debug("curdevs=%1", curdevs);
  770.  
  771.     integer i = 0;
  772.     integer count = 0;
  773.     list<string> ret = [];
  774.  
  775.     /* Hotpluggable devices */
  776.     if(IsHotplug(type) && !contains(curdevs, "")) {
  777.     y2debug("Added simple hotplug device");
  778.     count = count + 1;
  779.     ret = add(ret, "");
  780.     }
  781.  
  782.     /* Remaining numbered devices */
  783.     while(count < num) {
  784.     string ii = sformat("%1", i);
  785.     if(!contains(curdevs, ii)) {
  786.         ret = add(ret, ii);
  787.         count = count + 1;
  788.     }
  789.     i = i + 1;
  790.     }
  791.  
  792.     y2debug("Free devices=%1", ret);
  793.     return ret;
  794. }
  795.  
  796. /**
  797.  * Compute free devices
  798.  * @param type device type
  799.  * @param num how many free devices return
  800.  * @return num of free devices
  801.  * @example GetFreeDevices("eth", 2) -> [ 1, 2 ]
  802.  */
  803. global define list GetFreeDevicesOld(string type, integer num) {
  804.     y2debug("Devices=%1", Devices);
  805.     y2debug("type,num=%1,%2", type, num);
  806.     y2debug("Devices[%1]=%2", type, Devices[type]:$[]);
  807.  
  808.     list curdevs = Map::Keys(Devices[type]:$[]);
  809.     y2debug("curdevs=%1", curdevs);
  810.  
  811.     integer i = 0;
  812.     integer count = 0;
  813.     list ret = [];
  814.  
  815.     /* Hotpluggable devices */
  816.     if(IsHotplug(type) && !contains(curdevs, "")) {
  817.     y2debug("Added simple hotplug device");
  818.     count = count + 1;
  819.     ret = add(ret, "");
  820.     }
  821.  
  822.     /* Remaining numbered devices */
  823.     while(count < num) {
  824.     string ii = sformat("%1", i);
  825.     if(!contains(curdevs, ii)) {
  826.         ret = add(ret, ii);
  827.         count = count + 1;
  828.     }
  829.     i = i + 1;
  830.     }
  831.  
  832.     y2debug("Free devices=%1", ret);
  833.     return ret;
  834. }
  835.  
  836. /**
  837.  * Return free device
  838.  * @param type device type
  839.  * @return free device
  840.  * @example GetFreeDevice("eth") -> "1"
  841.  */
  842. global define string GetFreeDevice(string type) {
  843.     y2debug("type=%1", type);
  844.     list <string> freedevs = GetFreeDevices(type, 1);
  845.     string ret = (string) freedevs[0]:nil;
  846.     if(ret == nil) y2error("Free device location error: %1", ret);
  847.     y2debug("Free device=%1", ret);
  848.     return ret;
  849. }
  850.  
  851. /**
  852.  * Check presence of the device (alias)
  853.  * @param dev device identifier
  854.  * @return true if device is present
  855.  */
  856. global define boolean Check(string dev) {
  857.  
  858.     y2debug("Check(%1)", dev);
  859.     string typ = device_type(dev);
  860.     string num = device_num(dev);
  861.     string anum = alias_num(dev);
  862.     if (report_every_check) y2milestone("Check(%1,%2,%3)", typ, num, anum);
  863.  
  864.     if(!haskey(Devices, typ))
  865.     return false;
  866.  
  867.     map devsmap = Devices[typ]:$[];
  868.     if(!haskey(devsmap, num))
  869.     return false;
  870.  
  871.     /* FIXME NI: not needed?
  872.     Name = dev;
  873.     Current = (map) eval(devsmap[num]:$[]);
  874.     */
  875.  
  876.     if(anum != "") {
  877.     map devmap = devsmap[num]:$[];
  878.     map amap = devmap["_aliases"]:$[];
  879.     if(!haskey(amap, anum))
  880.         return false;
  881.     /* FIXME NI: not needed?
  882.     Current = (map) eval(amap[anum]:$[]);
  883.     alias = anum;
  884.     */
  885.     }
  886.  
  887.     y2debug("Check passed");
  888.     return true;
  889. }
  890.  
  891. /**
  892.  * Select the given device
  893.  * @param device to select ("" for new device, default values)
  894.  * @return true if success
  895.  */
  896. global define boolean Select(string name) {
  897.  
  898.     Name = "";
  899.     Current = $[];
  900.  
  901.     y2debug("name=%1", name);
  902.     if(name != "" && !Check(name)) {
  903.     y2error("No such device: %1", name);
  904.     return false;
  905.     }
  906.  
  907.     Name = name;
  908.     // FIXME NI: Current = Devices[device_type(Name), device_num(Name)]:$[];
  909.     // may be fixed already. or not: #39236
  910.     string t = device_type(Name);
  911.     Current = Devices[t, device_num(Name)]:$[];
  912.     string a = alias_num(Name);
  913.     if(a != nil && a != "") Current = Current["_aliases", a]:$[];
  914.  
  915.     if(Current == $[]) {
  916.     /* Default device map */
  917.     Current = $[
  918.         /* FIXME: remaining items */
  919.     ];
  920.     }
  921.  
  922.     y2debug("Name=%1", Name);
  923.     y2debug("Current=%1", Current);
  924.  
  925.     return true;
  926. }
  927.  
  928. /**
  929.  * Add a new device
  930.  * @return true if success
  931.  */
  932. global define boolean Add() {
  933.     operation = nil;
  934.     if(Select("") != true) return false;
  935.     operation = `add;
  936.     return true;
  937. }
  938.  
  939. /**
  940.  * Edit the given device
  941.  * @param dev device to edit
  942.  * @return true if success
  943.  */
  944. global define boolean Edit(string name) {
  945.     operation = nil;
  946.     if(Select(name) != true) return false;
  947.     operation = `edit;
  948.     return true;
  949. }
  950.  
  951. /**
  952.  * Delete the given device
  953.  * @param dev device to delete
  954.  * @return true if success
  955.  */
  956. global define boolean Delete(string name) {
  957.     operation = nil;
  958.     if(Select(name) != true) return false;
  959.     operation = `delete;
  960.     return true;
  961. }
  962.  
  963. /**
  964.  * Update Devices map
  965.  * @param dev device identifier
  966.  * @param newdev new device map
  967.  * @param check if check if device already exists
  968.  * @return true if success
  969.  */
  970. define boolean Change2(string name, ifcfg_t newdev, boolean check) {
  971.     y2debug("Change(%1,%2,%3)", name, newdev, check);
  972.     y2debug("Devices=%1", Devices);
  973.  
  974.     if(Check(name) && check) {
  975.     y2error("Device already present: %1", name);
  976.     return false;
  977.     }
  978.  
  979.     string t = device_type(name);
  980.     string d = device_num(name);
  981.     string a = alias_num(name);
  982.     y2debug("ChangeDevice(%1,%2,%3)", t, d, a);
  983.  
  984.     map<string, ifcfg_t> devsmap = Devices[t]:$[];
  985.     ifcfg_t devmap = devsmap[d]:$[];
  986.     map amap = devmap["_aliases"]:$[];
  987.  
  988.     if(a != "") {
  989.     amap[a] = newdev;
  990.     devmap["_aliases"] = amap;
  991.     }
  992.     else
  993.     devmap = newdev;
  994.  
  995.     devsmap[d] = devmap;
  996.     Devices[t] = devsmap;
  997.  
  998.     y2debug("Devices=%1", Devices);
  999.     return true;
  1000. }
  1001.  
  1002. define boolean Delete2(string name) {
  1003.  
  1004.     if(!Check(name)) {
  1005.     y2error("Device not found: %1", name);
  1006.     return false;
  1007.     }
  1008.  
  1009.     string t = device_type(name);
  1010.     string d = device_num(name);
  1011.     string a = alias_num(name);
  1012.     map<string, ifcfg_t> devsmap = Devices[t]:$[];
  1013.  
  1014.     if(a != "") {
  1015.     map amap = devsmap[d, "_aliases"]:$[];
  1016.     amap = remove(amap, a);
  1017.     devsmap[d, "_aliases"] = amap;
  1018.     }
  1019.     else
  1020.     devsmap = remove(devsmap, d);
  1021.  
  1022.     Devices[t] = devsmap;
  1023.  
  1024.     // Originally this avoided errors in the log when deleting an
  1025.     // interface that was not present at Read (had no ifcfg file).
  1026.     // #115448: OriginalDevices is not updated after Write so
  1027.     // returning to the network proposal and deleting a card would not work.
  1028.     if (true ||
  1029.        haskey(OriginalDevices, t) && haskey(OriginalDevices[t]:$[], d)) {
  1030.     y2milestone("Deleting file: %1", name);
  1031.     Deleted[size(Deleted)] = name;
  1032.     }
  1033.     else {
  1034.     y2milestone("Not deleting file: %1", name);
  1035.     y2debug("OriginalDevices=%1", OriginalDevices);
  1036.     y2debug("a=%1", a);
  1037.     }
  1038.  
  1039.     return true;
  1040. }
  1041.  
  1042. /**
  1043.  * Add the alias to the list of deleted items.
  1044.  * Called when exiting from the aliases-of-device dialog.
  1045.  * #48191
  1046.  */
  1047. global boolean DeleteAlias (string device, string aid) {
  1048.     string alias = sformat ("%1#%2", device, aid);
  1049.     y2milestone("Deleting alias: %1", alias);
  1050.     Deleted[size(Deleted)] = alias;
  1051.     return true;
  1052. }
  1053.  
  1054. global define boolean Commit() {
  1055.     y2debug("Name=%1", Name);
  1056.     y2debug("Current=%1", Current);
  1057.     y2debug("Devices=%1", Devices);
  1058.     y2debug("Deleted=%1", Deleted);
  1059.     y2debug("operation=%1", operation);
  1060.  
  1061.     if(operation == `add || operation == `edit) {
  1062.     Change2(Name, Current, operation == `add);
  1063.     }
  1064.     else if(operation == `delete) {
  1065.     Delete2(Name);
  1066.     }
  1067.     else {
  1068.     y2error("Unknown operation: %1 (%2)", operation, Name);
  1069.     return false;
  1070.     }
  1071.  
  1072.     y2debug("Devices=%1", Devices);
  1073.     y2debug("Deleted=%1", Deleted);
  1074.  
  1075.     Name = "";
  1076.     Current = $[];
  1077.     operation = nil;
  1078.  
  1079.     return true;
  1080. }
  1081.  
  1082. global define string GetValue(string name, string key) {
  1083.     if(!Select(name)) return nil;
  1084.     return Current[key]:"";
  1085. }
  1086.  
  1087. global define boolean SetValue(string name, string key, string value) {
  1088.     if(!Edit(name)) return nil;
  1089.     if(key == nil || key == "" || value == nil) return false;
  1090.     Current[key] = value;
  1091.     return Commit();
  1092. }
  1093.  
  1094. /**
  1095.  * Locate devices of the given type and value
  1096.  * @param key device key
  1097.  * @param val device value
  1098.  * @return list of devices with key=val
  1099.  */
  1100. global define list<string> Locate(string key, string val) {
  1101.     list<string> ret = [];
  1102.     maplist(string typ, map devsmap, Devices, {
  1103.     maplist(string num, map devmap, (map<string,map>) devsmap, {
  1104.         if(devmap[key]:"" == val) ret = add(ret, device_name(typ,num));
  1105.     });
  1106.     });
  1107.  
  1108.     return ret;
  1109. }
  1110.  
  1111. /**
  1112.  * Locate devices of the given type and value
  1113.  * @param key device key
  1114.  * @param val device value
  1115.  * @return list of devices with key!=val
  1116.  */
  1117. global define list<string> LocateNOT(string key, string val) {
  1118.     list<string> ret = [];
  1119.     maplist(string typ, map devsmap, Devices, {
  1120.     maplist(string num, map devmap, (map<string,map>) devsmap, {
  1121.         if(devmap[key]:"" != val) ret = add(ret, device_name(typ,num));
  1122.     });
  1123.     });
  1124.  
  1125.     return ret;
  1126. }
  1127.  
  1128. /**
  1129.  * Check if any device is using the specified provider
  1130.  * @param provider provider identification
  1131.  * @return true if there is any
  1132.  */
  1133. global define boolean LocateProvider(string provider) {
  1134.     list devs = Locate("PROVIDER", provider);
  1135.     return size(devs) > 0;
  1136. }
  1137.  
  1138. /**
  1139.  * Update /dev/modem symlink
  1140.  * @return true if success
  1141.  */
  1142. global define boolean UpdateModemSymlink() {
  1143.     boolean ret = false;
  1144.     if(contains(Map::Keys(Devices), "modem")) {
  1145.     list ml = Map::Keys(Devices["modem"]:$[]);
  1146.     string ms = ml[0]:"0";
  1147.     // map mm = Devices["modem"]:$[][ms]:$[];
  1148.     map mm = Devices["modem", ms]:$[];
  1149.     string mdev = mm["MODEM_DEVICE"]:"";
  1150.     if(mdev != "" && mdev != "/dev/modem") {
  1151.         string curlink = nil;
  1152.         map m = (map) SCR::Read(.target.lstat, "/dev/modem");
  1153.         if(m["islink"]:false == true)
  1154.         curlink = (string) SCR::Read(.target.symlink, "/dev/modem");
  1155.         if(curlink != mdev) {
  1156.         SCR::Execute(.target.symlink, mdev, "/dev/modem");
  1157.         ret = true;
  1158.         }
  1159.     }
  1160.     }
  1161.     return ret;
  1162. }
  1163.  
  1164. /**
  1165.  * Clean the hotplug devices compatibility symlink,
  1166.  * usually ifcfg-eth-pcmcia -> ifcfg-eth-pcmcia-0.
  1167.  * @return true if success
  1168.  */
  1169. global define boolean CleanHotplugSymlink() {
  1170.  
  1171.     list<string> types = [ "eth-pcmcia", "eth-usb", "tr-pcmcia", "tr-usb" ];
  1172.     maplist(string t, types, {
  1173.     string link = "/etc/sysconfig/network/ifcfg-" + t;
  1174.     y2debug("link=%1", link);
  1175.     map lstat = (map) SCR::Read(.target.lstat, link);
  1176.     if(lstat["islink"]:false == true) {
  1177.         string file = (string) SCR::Read(.target.symlink, link);
  1178.         file = "/etc/sysconfig/network/" + file;
  1179.         y2debug("file=%1", file);
  1180.         if(SCR::Read(.target.size, file) > -1) {
  1181.         y2milestone("Cleaning hotplug symlink");
  1182.         y2milestone("Devices[%1]=%2", t, Devices[t]:$[]);
  1183.         Devices[t] = remove(Devices[t]:$[], "");
  1184.         y2milestone("Devices[%1]=%2", t, Devices[t]:$[]);
  1185.         }
  1186.     }
  1187.     });
  1188.  
  1189.     y2debug("Devices=%1", Devices);
  1190.     return true;
  1191. }
  1192.  
  1193. /**
  1194.  * Get devices of the given type
  1195.  * @param type devices type ("" for all)
  1196.  * @return list of found devices
  1197.  */
  1198. global define list<string> List(string devregex) {
  1199.  
  1200.     list<string> ret = [];
  1201.     if(devregex == "" || devregex == nil) {
  1202.     maplist(string t, map d, Devices, {
  1203.         maplist(string n, (list<string>) Map::Keys(d), {
  1204.         ret[size(ret)] = device_name(t,n);
  1205.         });
  1206.     });
  1207.     }
  1208.     else {
  1209.     // it's a regex for type, not the whole name
  1210.     string regex = "^(" + DeviceRegex[devregex]:devregex + ")$";
  1211.     maplist(string t, map d, Devices, {
  1212.         if(regexpmatch(t, regex)) {
  1213.         maplist(string n, (list<string>) Map::Keys(d), {
  1214.             ret[size(ret)] = device_name(t,n);
  1215.         });
  1216.         }
  1217.     });
  1218.     /*
  1219.     map d = Devices[type]:$[];
  1220.     maplist(string n, Map::Keys(d), {ret[size(ret)] = device_name(type,n);});
  1221.     */
  1222.     }
  1223.  
  1224.     y2debug("ret=%1", ret);
  1225.     return ret;
  1226. }
  1227.  
  1228. /**
  1229.  * Find the fastest available device
  1230.  */
  1231. global define string Fastest() {
  1232.  
  1233.     string ret = "";
  1234.     list<string> devices = List("");
  1235.  
  1236.     /* Find the fastest device */
  1237.     foreach(integer num, string type, FastestTypes, {
  1238.     foreach(string dev, devices, {
  1239.         if(ret == "" && regexpmatch(dev, "^" + DeviceRegex[type]:"" + "[0-9]*$")) {
  1240.         //do some checking (whether the card is up and has cable connected)
  1241.         if(type == "netcard") {
  1242.             string cmd = "getcfg-interface " + dev;
  1243.             map dn =(map) SCR::Execute(.target.bash_output, cmd);
  1244.             string devname = deletechars(dn["stdout"]:"", "\n");
  1245.  
  1246.             cmd = "ethtool " + devname + " | grep \"Link detected: yes\"";
  1247.             string cmd2 = "ifstatus " + devname + " | grep \"" + devname + " is up\"";
  1248.  
  1249.             if((SCR::Execute(.target.bash, cmd) == 0) && (SCR::Execute(.target.bash, cmd2) == 0)) {
  1250.             y2milestone("%1 is up and connected", dev);
  1251.             ret = dev;
  1252.             }
  1253.             else y2milestone("%1 is down or disconnected", dev);
  1254.         }
  1255.         else ret = dev;
  1256.         }
  1257.     });
  1258.     });
  1259.  
  1260.     if (ret == "") {
  1261.     y2milestone("No suitable device found, falling back to the first device on the detected list");
  1262.     ret = devices[0]:"";
  1263.     }
  1264.  
  1265.     y2milestone("ret=%1", ret);
  1266.     return ret;
  1267. }
  1268.  
  1269. global define string FastestType(string name) {
  1270.     string ret = "";
  1271.     maplist(integer num, string type, FastestTypes, {
  1272.     string regex = DeviceRegex[type]:"";
  1273.     if (ret == "" && regexpmatch(name, "^" + regex + "[0-9]*$"))
  1274.     ret = type;
  1275.     });
  1276.     /*
  1277.     maplist(string typ, string regex, DeviceRegex, {
  1278.     if (ret == "" && regexpmatch(name, "^" + regex + "[0-9]*$"))
  1279.     ret = typ;
  1280.     });
  1281.     */
  1282.     return ret;
  1283. }
  1284.  
  1285. /**
  1286.  * Check if the given device has any virtual alias.
  1287.  * @param dev device to be checked
  1288.  * @return true if there are some aliases
  1289.  */
  1290. global define boolean HasAliases(string name) {
  1291.  
  1292.     if(!Check(name)) {
  1293.     y2error("Device not found: %1", name);
  1294.     return false;
  1295.     }
  1296.  
  1297.     string t = device_type(name);
  1298.     string d = device_num(name);
  1299.     string a = alias_num(name);
  1300.  
  1301.     return a == "" && Devices[t, d, "_aliases"]:$[] != $[];
  1302. }
  1303.  
  1304. /**
  1305.  * DSL needs to save its config while the underlying network card is
  1306.  * being configured.
  1307.  */
  1308. global define void Push() {
  1309.     if(stack != $[]) y2error("Stack not empty: %1", stack);
  1310.     stack["Name"] = Name;
  1311.     stack["Current"] = Current;
  1312.     stack["operation"] = operation;
  1313.     y2milestone("PUSH: %1", stack);
  1314. }
  1315.  
  1316. global define void Pop() {
  1317.     y2milestone("POP: %1", stack);
  1318.     Name = stack["Name"]:"";
  1319.     Current = stack["Current"]:$[];
  1320.     operation = (symbol) stack["operation"]:nil;
  1321.     stack = $[];
  1322. }
  1323.  
  1324. /**
  1325.  * #46803: forbid "/" (filename), maybe also "-" (separator) "_" (escape)
  1326.  */
  1327. global string ValidCharsIfcfg () {
  1328.     return String::ValidCharsFilename ();
  1329. }
  1330.  
  1331. /* EOF */
  1332. }
  1333.