14

When I open Emacs, it evaluates my init file, which includes refreshing my package archives via internet connection. This is problematic when I don't have an internet connection, thus I need to prevent execution of that code when starting Emacs without an internet connection. To solve this issue, I wonder if there is there a way to have Emacs ignore the package refresh code when I don't have an internet connection?

Here are the first few lines of my init.el:

;; Requisites: Emacs >= 24
(require 'package)
(package-initialize)

;; PACKAGE MANAGEMENT
(add-to-list 'package-archives 
  '("melpa" . "http://melpa.milkbox.net/packages/") t)

(package-refresh-contents)       

I imagine that I can add code to load my Emacs file as follows:

;; Requisites: Emacs >= 24
(when (connected-to-internet-p)   ; I need this predicate function
  (require 'package)
  (package-initialize)
  (add-to-list 'package-archives 
               '("melpa" . "http://melpa.milkbox.net/packages/") t)
  (package-refresh-contents))

Is there a (connected-to-internet) function, or similar approach, to resolve this issue?

Malabarba
  • 22,878
  • 6
  • 78
  • 163
modulitos
  • 2,432
  • 1
  • 18
  • 36
  • 2
    There is a related answer here http://stackoverflow.com/a/21065704/3170376. – Name Jan 23 '15 at 09:16
  • 2
    Why do you want to refresh the package archives when you start Emacs? – phils Jan 23 '15 at 11:10
  • @Name That's more than related. It's *the* answer (assuming it works). – Malabarba Jan 23 '15 at 20:52
  • 1
    I would strongly advise that you do not run package-refresh-contents on each startup. It's most likely that you'll need to run it once when first pulling down your config on a new machine and then you wont need it again for months. Doing it when you have a connection is the wrong answer to this problem, the real problem is that you're running it at all when you don't need to be. – Jordon Biondo Jan 30 '15 at 15:31

7 Answers7

8

A simple solution that I've adopted from my shell scripts is

(defun internet-up-p (&optional host)
    (= 0 (call-process "ping" nil nil nil "-c" "1" "-W" "1" 
                       (if host host "www.google.com"))))

You can test this in the *scratch* buffer:

(message (if (internet-up-p) "Up" "Down"))
"Up"
Stefan
  • 26,154
  • 3
  • 46
  • 84
Tyler Earnest
  • 81
  • 1
  • 2
7

Well, if you still wanted to refresh contents automatically, whenever possible, you could do something like the code below:

(defun can-retreive-packages ()
  (cl-loop for url in '("http://marmalade-repo.org/packages/"
                        "http://melpa.milkbox.net/packages/"
                        "http://elpa.gnu.org/packages/")
           do (condition-case e
                  (kill-buffer (url-retrieve-synchronously url))
                (error (cl-return)))
           finally (cl-return t)))

Few notes are due:

  1. This is slow, and it will be slow when starting up normally, so I rather do it manually.
  2. There is no way to test for internet connection in general. You can only discover that you aren't able to connect to a particular service after some amount of time that you've tried. This is also one of the reasons why it is so slow.
  3. The code is more of an illustration of how to approach the problem. You could've easily done (ignore-errors (package-refresh-contents)) if you didn't care whether it succeeded or not.
wvxvw
  • 11,222
  • 2
  • 30
  • 55
  • That's clearly the right way to do it. At any one time, some parts of the Internet are accessible and some are not, and the proper way to deal with it is to probe for connectivity. – jch Jan 24 '15 at 12:00
  • 1
    This will also create a bunch of large unseen buffers, it would be best do (kill-buffer (url-ret...)) – Jordon Biondo Jan 30 '15 at 15:27
  • @JordonBiondo ok, point taken. Haven't thought about it. – wvxvw Jan 30 '15 at 17:04
4

One thing you could try is the function network-interface-list. It returns an alist of network interfaces and their IP addresses.

For me, this is what it returns when I'm connected to both Ethernet and wifi:

(("en5" .
  [10 151 0 63 0])
 ("en0" .
  [10 151 2 76 0])
 ("lo0" .
  [127 0 0 1 0]))

And when I turn off wifi, en0 disappears:

(("en5" .
  [10 151 0 63 0])
 ("lo0" .
  [127 0 0 1 0]))

Experiment with that and see what you get when you don't have an Internet connection. For example, to only refresh packages when en0 is up, do something like:

(when (assoc "en0" (network-interface-list))
  (package-refresh-contents))
legoscia
  • 6,012
  • 29
  • 54
  • That's an interesting function. I get `(("eth0" . [10 72 153 234 0]) ("lo" . [127 0 0 1 0]))` because I am connected to ethernet. – Kaushal Modi Jan 23 '15 at 13:33
3

To expand on Legoscia's answer:

(defun test-internet ()
  (remove-if (lambda (el)
                   (string-match-p "lo.*" (car el)))
                 (network-interface-list)))

This will return a list of active network connections (lo.* is the loopback interface, in some cases lo in others lo#.

If the test returns non-nil, then there is a network connection (wifi/ethernet, no guarantee it actually reaches the outside internet however. Would have to ping somewhere as a test for that), if it returns nil then there is no way to retrieve the package list.

Jonathan Leech-Pepin
  • 4,307
  • 1
  • 19
  • 32
2

On a modern Linux system with DBus and NetworkManager:

(defun nm-is-connected()
  (equal 70 (dbus-get-property
             :system "org.freedesktop.NetworkManager" "/org/freedesktop/NetworkManager"
             "org.freedesktop.NetworkManager" "State")))
db48x
  • 15,741
  • 1
  • 19
  • 23
2

I use the following to exclude loopback interfaces and also VirtualBox and Docker interfaces. I hope it's helpful.

(defun tzz-has-network ()
  (remove-if (lambda (i)
               (or (string-match-p "\\(vboxnet\\|docker\\).*" i)
                   (member 'loopback (nth 4 (network-interface-info i)))))
             (mapcar 'car (network-interface-list))))
Ted Zlatanov
  • 404
  • 2
  • 4
1

I think you're looking at it the wrong way. If you really want to auto-update your packages, then don't do it synchronously at startup: do it from an idle timer of some sort. E.g.

(run-with-idle-timer 10 nil
  (lambda ()
    (package-refresh-contents)
    ..etc..))
Stefan
  • 26,154
  • 3
  • 46
  • 84