class: middle # A shell that shouts at you ## { EnthusiastiCon } ## Jan-Erik Rediger — [@badboy_](https://twitter.com/badboy_) — 2019-05-25
??? Hi! I'm here to talk about a shell that shouts at you. Disclaimer: I'm not gonna shout on stage. This is also about how I learned a bit more about Linux and files and how a tweet got me into writing 100 lines of Rust code. --- > @Argorak tweeted: > > stdshout > > (Feb 28, 2017) .footnote[[Tweet Link](https://twitter.com/Argorak/status/836532440420204544)] ??? 2 years ago a friend of mine, florian aka argorak tweeted a single word: stdshout. --- > @pobocks replying to [@Argorak](https://twitter.com/Argorak/status/836532440420204544): > > now I kind of want a magic fd that assumes utf-8 and upcases everything and appends !!!1! to it. > > (Feb 28, 2017) .footnote[[Tweet Link](https://twitter.com/pobocks/status/836534631822409728)] ??? and someone else replied: [read tweet] --- class: middle, center # Well ... ??? Well [pause] --- class: middle # What if ...? #
we can do that for every output?
??? What if [pause] --- class: middle # What if ... # we can do that for every output? ??? What if we can do that for every output of every program started from your shell --- # Everything is a file ??? as you might have heard everything is a file --- # Everything is a file descriptor ??? and everything is identified by a file descriptor actually it's just a number --- # Everything is a file description ??? Now to extend the confusion the linux kernel has file descriptions internally --- ??? even if you never really directly used file descriptors you have interacted with the standard ones --- # stdin ??? 3 exist stdin is the input usually your keyboard -- # stdout ??? stdout is the output that's what you see in your terminal when you print stuff -- # stderr ??? and then there is stderr it's also connected to the output, your terminal but with small differences --- # stdin = 0 # stdout = 1 # stderr = 2 ??? now all three actually have file descriptors which are just numbers zero one two and none of these are actually special they act just the same as a file descriptor for a file --- class: center, middle # close(stdout) ??? for example you can stdout --- class: center, middle # puts("Hello, World!") ??? now what happens if you try to print something to the terminal? --- ??? [pause] nothing --- background-image: url(media/were-closed.jpg) background-size: cover .sfootnote[by [Nick Papakyriazis](https://www.flickr.com/photos/mlrs193/6015396482), [CC BY-NC-SA](https://creativecommons.org/licenses/by-nc-sa/2.0/)] ??? we closed stdout we can't print to it anymore --- class: center, middle # fd = open("file.txt", O_CREAT, S_IRUSR) ??? let's open another file called file.txt yes, that is the low-level POSIX way to open a file in C --- class: center, middle # fd = ? ??? what is fd now? yes, it is a number but which? --- class: center, middle # fd = 1 ??? it's 1 linux is pretty simple it just picks the lowest unused number which for us is 1 remember: we previously closed what used to be 1 --- class: center, middle # puts("Hello, World!") ??? now we print again and what happens? --- ??? exactly we still see nothing stdout or fd 1 as we call it is not our terminal anymore --- class: middle # $ cat file.txt # Hello, World! ??? there we have our output it's in the file we created puts just writes to fd 1 and we controlled what is fd 1 --- class: middle # ... and now for something completely different ??? but now for something completely different --- class: middle, center # Run before everything else ??? to affect every program's output we need to run before everything else how do we do this --- class: middle, center # LD_PRELOAD .footnote[Jess Fraz's [LD_PRELOAD: The Hero We Need and Deserve](https://blog.jessfraz.com/post/ld_preload/)] ??? turns out there's a special environment variable that we can use LD_PRELOAD LD_PRELOAD allows you to specify a library and that gets run before any other library even the standard library or the program --- ## Things LD_PRELOAD can do .footnote[Jess Fraz's [LD_PRELOAD: The Hero We Need and Deserve](https://blog.jessfraz.com/post/ld_preload/)] ??? so what can you do with this? all sorts of things -- * Time travel ??? you can modify time functions and travel in time -- * Changing the environment ??? you can change everything in the environment -- * Make Dropbox work on non-ext4 ??? you can even make dropbox think it runs on ext4 -- * Disabling TLS cert validation ??? or disable tls certificate validation. dont do this -- * ... everything else ??? but it's just code so you can pretty much do anything --- class: middle, center # Run before everything else? ??? LD_PRELOAD loads libraries. a library doesn't have a main function how do we then run code before everything else? --- class: center, middle, h1small # \_\_attribute\_\_((constructor)) .footnote[docs:
] ??? We can use a not well known attribute to create a "constructor" The constructor attribute causes the function to be called automatically before execution enters main () --- class: middle # ... and now for something completely different ??? but now for something completely different --- class: middle, center # STDSHOUT!!!1! .footnote[
/badboy/stdshout] ??? stdshout bang bang bang one bang the original request --- # $ echo hello, enthusiasticon | stdshout # HELLO, ENTHUSIASTICON!!!1! ??? we wanted something that takes every input turns it to uppercase and append bang bang bang one bang I am a rust developer and in Rust this is a rather simple task --- ## "hello enthusiasticon".to_uppercase() ## # => "HELLO ENTHUSIASTICON" ??? turning a string into all uppercase is just calling a function called to_uppercase on it --
hidden
## "hello enthusiasticon".to_uppercase() + "!!!1!" ## # => "HELLO ENTHUSIASTICON!!!1!" ??? appending a bang bang bang one bang is just as easily done with the add operation --- class: middle ``` fn shout(msg: &str) -> String { msg.to_uppercase() + "!!!1!" } ``` ??? we can now put this into a function ... --- class: middle ``` fn main() { puts(shout(stdin.read())); } ``` ??? then we can read from stdin use our function to shout and print it back out to stdout --- # $ echo hello, enthusiasticon | stdshout # HELLO, ENTHUSIASTICON!!!1! ??? and there we have stdshout --- class: middle # Run before everything else? ??? now the second part of the request was doing that for every output of every program ... and we now know how to run before everything else --- class: middle, center, h1small # LD_PRELOAD!!!1! ??? using LD_PRELOAD bang bang bang one bang -- # & \_\_attribute\_\_((constructor)) ??? and the constructor attribute --- ``` #[constructor] fn start_stdhout() { // start shouting } ``` ??? now putting this all together we build a library using the constructor and start shouting but that middle part? --- class: middle # Do something for every output? ??? we want to do it for every output of every program how do we do that? we learned about it before! --- class: middle, center # File Descriptor Magic ??? file descriptor magic we can close and reopen and redirect as needed --- class: center, fullimg ![](normal-app-run.png) .footnote[Keyboard by worker, screen by Adrien Coquet,
application by Arief Sugiyanto from the [Noun Project](https://thenounproject.com/)] ??? so before we had our normal program stdin comes from the keyboard goes into the program the program prints and that goes to stdout --- class: center, fullimg ![](redirected-app-run.png) .footnote[Keyboard by worker, screen by Adrien Coquet,
application by Arief Sugiyanto from the [Noun Project](https://thenounproject.com/)] ??? now with LD_PRELOAD we inject ourselves and do some of this file descriptor magic before the application launches we start a stdshout instance and redirect the applications stdout into stdshout's stdin which outputs into the real stdout, your terminal yes, this is essentially just a pipe yes, you can achieve this in an easier way --- class: h1small # $ LD_PRELOAD=stdshout.so bash ??? but we don't go the easy way so we now use LD_PRELOAD on our stdshout library and start a shell --- # $ echo hello, enthusiasticon # HELLO, ENTHUSIASTICON!!!1! ??? and finally it all works! we have a working stdshout for every programs output and when I say every program I mean every program --- class: h1small # $ git status # ON BRANCH MASTER!!!1! # YOUR BRANCH IS UP TO DATE WITH 'ORIGIN/MASTER'!!!1! ??? even git --- background-size: contain background-image: url(media/git-log.png) ??? the nice thing about also having a stdshout program now we can use it as input for other programs and make our commit messages more on point --- > @Argorak tweeted: > > stdshout > > (Feb 28, 2017) .footnote[[Tweet Link](https://twitter.com/Argorak/status/836532440420204544)] ??? and this is how a tweet with jus a single word led me down a long path of understanding file descriptors and linux a little bit better so whenever you come across a small silly idea ... maybe just implement it you might learn something --- class: center, withimg # RustFest Barcelona 2019 ![RustFest](media/rustfest.png) ##
??? quick side info: I'm one of the Rustfest organizers and later this year RustFest will happen in Barcelona and I invite you all to join us and learn about Rust --- class: middle, center # A shell that shouts at you ## [STDSHOUT!!1!](https://github.com/badboy/stdshout) ##
/badboy/stdshout ## Jan-Erik Rediger — [@badboy_](https://twitter.com/badboy_) ??? thank you.