22
\$\begingroup\$

XKCD: Sandwich

Challenge: Write a makefile, which would lead to this behavior:

% make me a sandwich
What? Make it yourself
% sudo make me a sandwich
Okay

Rules:

  1. Should work on a Linux machine, where the user isn't root but allowed to use sudo.
  2. Must not say Okay to any shorter make command (e.g. sudo make sandwich).
  3. The second command should work because of sudo, not because it's the second invocation.

Guidelines:

  1. Keep it short (after all, it's a Code Golf site).
  2. It would be nice if it would actually make a sandwich.
  3. Try to cleverly hide why sudo makes a difference.

The winner will be decided by votes.

\$\endgroup\$
3
  • 2
    \$\begingroup\$ savannah.gnu.org/bugs/?23273 \$\endgroup\$
    – ardnew
    Jul 30, 2013 at 23:26
  • \$\begingroup\$ @ardnew, unfortunately, I specifically requested to write a makefile, which, I think, invalidates solutions based on patching make. \$\endgroup\$
    – ugoren
    Jul 31, 2013 at 5:53
  • \$\begingroup\$ totally agree, @ugoren. didnt mean for that comment to dismiss the challenge or suggest a solution \$\endgroup\$
    – ardnew
    Jul 31, 2013 at 13:48

5 Answers 5

11
\$\begingroup\$

Okay, I'll bite. This doesn't necessarily adhere to guideline #3, but it does a fair job on the other two guidelines. It also cleans up after itself, as any good sandwich-maker should.

.SILENT:

%:%.c
        $(CC) -o $@ $^ && rm -f $^

default:
        echo 'The makings for a sandwich are here.'

a.c:
        echo '#include <stdio.h>' > a.c
        echo 'int main() {' >> a.c
        echo '  char *txt[] = { "What? Make it yourself.", "Okay." };' >> a.c
        echo '  int n = 0;' >> a.c
        echo '  while (getchar() != EOF) n ^= 1;' >> a.c
        echo '  puts(txt[n]); return 0; }' >> a.c

me.c:
        echo '#include <stdio.h>' > me.c
        echo '#include <unistd.h>' >> me.c
        echo 'int main() {' >> me.c
        echo '  char *txt[] = { "/dev/null", "sandwich" };' >> me.c
        echo '  puts(txt[!getuid()]); return 0; }' >> me.c

sandwich:
        ./me | ./a | tee `./me`
        rm -f me a

clean:
        rm -f sandwich

(Less buggy, and hopefully a bit less boring, than my initial submission.)

\$\endgroup\$
4
  • \$\begingroup\$ Looks like it violates rule #2 - sudo make me sandwich. \$\endgroup\$
    – ugoren
    Jul 30, 2013 at 5:15
  • \$\begingroup\$ && test -e ./a \$\endgroup\$ Jul 30, 2013 at 6:50
  • \$\begingroup\$ Argh, you're right. This version should do a better job of observing the rules. \$\endgroup\$
    – breadbox
    Jul 30, 2013 at 8:14
  • 1
    \$\begingroup\$ you certainly have the most appropriate name, @breadbox \$\endgroup\$
    – ardnew
    Jul 31, 2013 at 13:55
9
\$\begingroup\$

A fairly compact solution that ignores guideline #2 (127 characters, including some unnecessary whitespace). I follow breadbox's practice of cleaning up after myself. Don't put any other file in the same directory as the makefile! Also, note that the makefile must be called Makefile, other names will not work.

me: ; echo echo Okay >$@
a: ; chmod u=x me
sandwich: ; ./me 2>/dev/null || echo 'What? Make it yourself'; rm -f [!M]*
.SILENT:

Usage:

$ make me a sandwich
What? Make it yourself
$ sudo make me a sandwich
Okay
$ sudo make me sandwich  
What? Make it yourself
$ sudo make a sandwich
chmod: cannot access `me': No such file or directory
make: *** [a] Error 1
$ sudo make sandwich  
What? Make it yourself

I like the way this one deals with guideline #3. Just one press of Shift makes the difference.

\$\endgroup\$
4
\$\begingroup\$

Not mine, and not a true solution as it violates rule #2, but it has a very interesting story that led me here:

# With apologies to XKCD #149
me:
    @:
a:
    @:
sandwich:
    @[ "$$(id -u)" -eq 0 ] && { echo "Okay"; touch sandwich; } || echo "What? Make it yourself!"

So, where this came from?

I was just messing with the cloud-init script /boot/user-data in Ubuntu 22.04 Server image for Raspberry Pi, when I came into this example snippet:

## Write arbitrary files to the file-system (including binaries!)
#write_files:
#- path: /etc/default/console-setup
#  content: |
#    # Consult the console-setup(5) manual page.
#    ACTIVE_CONSOLES="/dev/tty[1-6]"
#    CHARMAP="UTF-8"
#    VIDEOMODE=
#    FONT="Lat15-Terminus18x10.psf.gz"
#    FONTFACE=
#    FONTSIZE=
#    CODESET="Lat15"
#  permissions: '0644'
#  owner: root:root
#- encoding: gzip
#  path: /root/Makefile
#  content: !!binary |
#    H4sICF2DTWIAA01ha2VmaWxlAFNWCM8syVBILMjPyU/PTC1WKMlXiPB2dlFQNjSx5MpNteLi
#    dLDiSoRQxYl5KeWZyRkgXrSCkoqKRmaKgm6pppKCbmqhgoFCrIKamkK1QmpyRr6Ckn92YqWS
#    NdC80uQMBZhOa4VahZoaqIrwjMQSewXfxOxUhcwShcr80qLi1Jw0RSUuAIYfEJmVAAAA
#  owner: root:root
#  permissions: '0644'

That binary blob caught my eye... was that an executable? A binary Makefile? It made no sense. Assuming this was plain text with base64 encoding, I tried in a terminal:

$ txt='H4sICF2DTWIAA01ha2VmaWxlAFNWCM8syVBILMjPyU/PTC1WKMlXiPB2dlFQNjSx5MpNteLi
> dLDiSoRQxYl5KeWZyRkgXrSCkoqKRmaKgm6pppKCbmqhgoFCrIKamkK1QmpyRr6Ckn92YqWS
> NdC80uQMBZhOa4VahZoaqIrwjMQSewXfxOxUhcwShcr80qLi1Jw0RSUuAIYfEJmVAAAA'
$ printf "$txt" | base64 -d
]�MbMakefileS�,�PH,���O�L-V(�W��vvQP64���M���t��J�Pʼny)��� ^�����Ff��n����nj���B����B�BjrF���vb��5м��
                                                                                                      �Nk�Z�������{���T�����Ң�Ԝ4E%.���

Ouch, a true binary indeed! What the hell..

Oh, wait... encoding: gzip. Hummm...

$ printf "$txt" | base64 -d | gunzip

Ta-da! The Makefile above appears!

What a convoluted Easter Egg from... well, I have no idea! And, what was that all about?

So I read the comic, experimented a bit with that Makefile, finally understood the joke, then googled for "make me a sandwich", and finally arrived here!

\$\endgroup\$
1
  • 2
    \$\begingroup\$ Welcome to Code Golf! This is certainly an unconventional way to arrive at a solution :p \$\endgroup\$ May 12, 2022 at 18:14
3
\$\begingroup\$

This solution doesn't adhere to guideline #2 — or does being vaguely sandwich-shaped count? I went on a kind of palindrome theme at the beginning, but I could only find so many ways to make lines symmetric without resorting to lame real code # ecod laer. I didn't take guideline #1 very seriously, aesthetics comes first.

Remember that it's a makefile; the 3 successive lines beginning with if, rm and echo begin with a tab. The other indented lines begin with spaces.

me : mr ; env | grep -q DO_US || rm -- mr || SU_OD q- perg | vne ; rm : em
                            .SILENT:##:TNELIS.
                              . = a.  .a = .
$(.:.=):;:>$@
sandwich:
    if expr>/dev/null $$(id -u); then echo 'What? Make it yourself'; fi
    rm f- a me mr 2>/dev/null
    echo Okay
mr : ; true : ; touch me : em f- mr || rm -f me : em hcuot ; : eurt ; : rm

Usage (the commands can be issued in any order):

$ sudo make me a sandwich     
Okay
$ sudo make me sandwich  
make: *** [sandwich] Error 1
$ sudo make a sandwich 
make: *** [sandwich] Error 1
$ sudo make sandwich  
make: *** [sandwich] Error 1
$ make me a sandwich
What? Make it yourself
make: *** [sandwich] Error 1

As for guideline #3, this isn't cleverly hidden, just slightly weirdly. Some hints:

There are two different tests: one search for SUDO_USER, and one test of whether id -u prints 0. Do you see why What? is not printed for root? Do you see how the lack of SUDO_USER causes Okay not to be printed?

\$\endgroup\$
2
\$\begingroup\$

Here is a first go using a different and simpler technique:

.SILENT:

reset_command:
    @rm -f command

me a sandwich: reset_command
    @echo $@ >> command
    -@make `cat command | tr "\n" "_"` 2>/dev/null

me_a_sandwich_: reset_command
    @if [ `whoami` == "root" ]; then echo "Okay"; else echo "What? Make it yourself."; fi

I'm pretty sure there is more that I can do with this - get it to respond to other input commands for instance. I'll have to work on the root switch obfuscation and the actual sandwich making.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.