Skip to content

Commit 12491f6

Browse files
committed
make self-update work on Linux
1 parent 1d54d0e commit 12491f6

3 files changed

Lines changed: 70 additions & 16 deletions

File tree

src/commands/tunnels.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
use std::process::Stdio;
7-
86
use async_trait::async_trait;
97
use tokio::sync::oneshot;
108

@@ -215,6 +213,10 @@ async fn serve_with_csa(
215213
csa: CodeServerArgs,
216214
shutdown_rx: Option<oneshot::Receiver<()>>,
217215
) -> Result<i32, AnyError> {
216+
// Intentionally read before starting the server. If the server updated and
217+
// respawn is requested, the old binary will get renamed, and then
218+
// current_exe will point to the wrong path.
219+
let current_exe = std::env::current_exe().unwrap();
218220
let platform = spanf!(log, log.span("prereq"), PreReqChecker::new().verify())?;
219221

220222
let auth = Auth::new(&paths, log.clone());
@@ -244,11 +246,8 @@ async fn serve_with_csa(
244246
// reuse current args, but specify no-forward since tunnels will
245247
// already be running in this process, and we cannot do a login
246248
let args = std::env::args().skip(1).collect::<Vec<String>>();
247-
let exit = std::process::Command::new(std::env::current_exe().unwrap())
249+
let exit = std::process::Command::new(current_exe)
248250
.args(args)
249-
.stdout(Stdio::inherit())
250-
.stderr(Stdio::inherit())
251-
.stdin(Stdio::inherit())
252251
.spawn()
253252
.map_err(|e| wrap(e, "error respawning after update"))?
254253
.wait()

src/util/tar.rs

Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,54 @@
55
use crate::util::errors::{wrap, WrappedError};
66

77
use flate2::read::GzDecoder;
8-
use std::fs::File;
8+
use std::fs;
9+
use std::io::{Seek, SeekFrom};
910
use std::path::{Path, PathBuf};
1011
use tar::Archive;
1112

1213
use super::io::ReportCopyProgress;
1314

15+
fn should_skip_first_segment(file: &fs::File) -> Result<bool, WrappedError> {
16+
// unfortunately, we need to re-read the archive here since you cannot reuse
17+
// `.entries()`. But this will generally only look at one or two files, so this
18+
// should be acceptably speedy... If not, we could hardcode behavior for
19+
// different types of archives.
20+
21+
let tar = GzDecoder::new(file);
22+
let mut archive = Archive::new(tar);
23+
let mut entries = archive
24+
.entries()
25+
.map_err(|e| wrap(e, "error opening archive"))?;
26+
27+
let first_name = {
28+
let file = entries
29+
.next()
30+
.expect("expected not to have an empty archive")
31+
.map_err(|e| wrap(e, "error reading entry file"))?;
32+
33+
let path = file.path().expect("expected to have path");
34+
35+
path.iter()
36+
.next()
37+
.expect("expected to have non-empty name")
38+
.to_owned()
39+
};
40+
41+
let mut had_multiple = false;
42+
for file in entries {
43+
if let Ok(file) = file {
44+
had_multiple = true;
45+
if let Ok(name) = file.path() {
46+
if name.iter().next() != Some(&first_name) {
47+
return Ok(false);
48+
}
49+
}
50+
}
51+
}
52+
53+
Ok(had_multiple) // prefix removal is invalid if there's only a single file
54+
}
55+
1456
pub fn decompress_tarball<T>(
1557
path: &Path,
1658
parent_path: &Path,
@@ -19,12 +61,15 @@ pub fn decompress_tarball<T>(
1961
where
2062
T: ReportCopyProgress,
2163
{
22-
let tar_gz = File::open(path).map_err(|e| {
23-
wrap(
24-
Box::new(e),
25-
format!("error opening file {}", path.display()),
26-
)
27-
})?;
64+
let mut tar_gz = fs::File::open(path)
65+
.map_err(|e| wrap(e, format!("error opening file {}", path.display())))?;
66+
let skip_first = should_skip_first_segment(&tar_gz)?;
67+
68+
// reset since skip logic read the tar already:
69+
tar_gz
70+
.seek(SeekFrom::Start(0))
71+
.map_err(|e| wrap(e, "error resetting seek position"))?;
72+
2873
let tar = GzDecoder::new(tar_gz);
2974
let mut archive = Archive::new(tar);
3075

@@ -37,7 +82,17 @@ where
3782
.path()
3883
.map_err(|e| wrap(e, "error reading entry path"))?;
3984

40-
let path = parent_path.join(entry_path.iter().skip(1).collect::<PathBuf>());
85+
let path = parent_path.join(if skip_first {
86+
entry_path.iter().skip(1).collect::<PathBuf>()
87+
} else {
88+
entry_path.into_owned()
89+
});
90+
91+
if let Some(p) = path.parent() {
92+
fs::create_dir_all(&p)
93+
.map_err(|e| wrap(e, format!("could not create dir for {}", p.display())))?;
94+
}
95+
4196
entry
4297
.unpack(&path)
4398
.map_err(|e| wrap(e, format!("error unpacking {}", path.display())))?;

src/util/zipper.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ fn should_skip_first_segment(archive: &mut ZipArchive<File>) -> bool {
4141
}
4242
}
4343

44-
true
44+
archive.len() > 1 // prefix removal is invalid if there's only a single file
4545
}
4646

4747
pub fn unzip_file<T>(path: &Path, parent_path: &Path, mut reporter: T) -> Result<(), WrappedError>
@@ -59,7 +59,7 @@ where
5959
} else {
6060
0
6161
};
62-
62+
println!("len: {}", archive.len());
6363
for i in 0..archive.len() {
6464
reporter.report_progress(i as u64, archive.len() as u64);
6565
let mut file = archive

0 commit comments

Comments
 (0)