Desafio: escreva um pedaço de código que se encerre [fechado]


39

Estou procurando (estou?) Por um pedaço de código que saia imediatamente - de uma maneira absolutamente não convencional.

Isso não significa: System.exit((int) 'A');(Java).

Isso pode significar:

#!/usr/bin/env python3
# NOTE: This kills ALL RUNNING Python processes. Be careful!
def exit():
    import os
    os.system("killall python3")
    # Windows addon
    os.system("taskkill /im python.exe /f")
exit()

A resposta mais votada vence! Todas as línguas, todas as arquiteturas.

Edit: Parar de lançar exceções não será mais aceito!


3
Mas isso não é convencional para finalizar a execução ... Bem, é uma alternativa exit(), mas ainda assim é um recurso implementado ...
s3lph

13
Isso não é trollagem de código - sabemos que queremos respostas estranhas a partir desta.
Liam Dawson

6
Desligar o sistema funciona?
Hosch250

8
Certa vez, causei acidentalmente uma placa de rede ao DMA pelo sistema operacional. Quando aconteceu, você voltou instantaneamente ao BIOS, reiniciando.
Ben Jackson

3
Eu sinto que Shannon nos venceu aqui;)
estrela brilhante

Respostas:


44

bash, 6 caracteres

exec [

execsubstitui o processo atual por outra coisa. [é o comando mais curto que eu achei inofensivo (é um apelido para test)


Outra exceção?
Johannes Kuhn

Não, apenas exec'ing o [comando :)
Dennis Kaarsemaker

1
por que está funcionando? o que faz?
S3lph

@the_Seppi que substitui o atual processo com [(aka test)
Timtech

~ $ echo $PATH/?-> /bin/X /bin/[ /bin/w. Casca de peixe me deu dois outros comandos. wé interessante na minha opinião.
Konrad Borowski

46

Bater

echo "Turn off your computer or i'll wipe your harddrive..."
echo 3;sleep 1
echo 2;sleep 1
echo 1;sleep 1
dd if=/dev/random of=/dev/hda

Encerra o programa o mais rápido que o usuário pode reagir ;-)


10
Voto positivo porque me fez rir.
Michael Stern

5
Melhor não executá-lo no Linux.
Kenorb # 02/07

1
dd: / dev / hda: not found
Joshua

39

Código vermelho

(Contexto: Redcode é a linguagem de pseudo-montagem usada no jogo de programação Core War , introduzido por AK Dewdney em 1984. Geralmente, faz uso pesado de código auto-modificável. Escrevi um pequeno e agradável tutorial sobre programação de Redcode há alguns anos. )

MOV 1, 0

Este programa de instrução única não apenas se mata, mas também limpa seu próprio código de programa da memória, deixando nenhum rastro na memória.

Chato, você diz? Afinal, o programa acima teria morrido de qualquer maneira, mesmo que não tivesse sobrescrito seu código. OK, aqui está uma que limpa todo o núcleo antes de finalmente se limpar e morrer:

loop: MOV  ptr+1, >ptr 
      JMN  loop, loop
      SPL  1
      SPL  1
      MOV  ptr+1, >ptr
ptr:  DAT  0, 1

As duas primeiras instruções realmente fazem a maior parte do trabalho: é apenas um loop de cópia / salto simples que copia a célula de instrução em branco (inicializada DAT 0, 0) após o final do programa para cada célula subsequente (usando o modo de endereçamento indireto pós-incremento >), até que o ponteiro finalmente passe para o início da memória e substitua o MOVpróprio loop. JMNQuando isso acontece, o (JuMp se não zero) o detecta e cai.

O problema é que isso ainda não JMNse limpa. Para nos livrarmos dele, precisamos de outro MOVpara limpar o JMN... mas isso significa que precisamos de outro MOVpara limpar isso MOV , e assim por diante. Para fazer com que o programa inteiro desapareça sem deixar rastro, precisamos de alguma forma organizar uma única MOVinstrução para limpar a si mesma e pelo menos uma outra instrução.

É aí que SPLentra - é um dos códigos mais estranhos do Redcode. Basicamente, é uma instrução "Ramificar as duas formas". Você vê, em vez de um simples registro de "contador de programas", como qualquer CPU normal teria, a VM Redcode possui uma "fila de processos": uma lista cíclica de indicadores para instruções a serem executadas. Normalmente, em cada ciclo, um ponteiro de instrução é deslocado para fora da cabeça da fila, a instrução é executada e a próxima instrução (a menos que tenha havido um salto ou uma instrução ilegal) é empurrada para a cauda da fila. Mas SPLfaz com que a próxima instrução e a instrução de destino fornecida (que, no caso de SPL 1, também sejam a próxima instrução) sejam empurradas para a fila.

O resultado disso é que, depois que as duas SPL 1instruções foram executadas, agora existem quatro processos na fila, todos sobre a execução da última MOV. Isso é apenas o suficiente para limpar o JMN, se SPLe o MOVpróprio, e também deixa a ptrcélula de instrução como DAT 0, 0- indistinguível do núcleo vazio ao seu redor.

(Como alternativa, poderíamos ter substituído a ptrinstrução por MOV 1, 1, a qual teria sido convertida MOV 1, 0pelas instruções anteriores e, portanto, seria eliminada, como o primeiro programa acima.)

Ps. Se você quiser testar esses programas, baixe um simulador de Redcode (também conhecido como MARS). Eu recomendaria o CoreWin ou o venerável pMARS , embora também existam vários outros bons simuladores.


Isso simplesmente não gera uma violação de acesso?
Danny Varod

12
Violação de acesso? Que tipo de coisa estranha é essa? (. A sério, Redcode é executado em uma máquina virtual bastante sumário Tudo abordar é relativo, o espaço de endereço é contíguo (e cíclica) e cada endereço é válido.)
Ilmari Karonen

O MOV 1, 0 copia 1 para o endereço 0 ou o contrário? Em todas as linguagens assembler, sei que o endereço 0 é ilegal (endereço NULL).
Danny Varod

4
A sintaxe é MOV source, dest. Mas como eu disse, todo endereçamento no Redcode é relativo, então o endereço 0realmente significa "o endereço desta instrução", enquanto o endereço 1realmente significa "o endereço desta instrução + 1". (E o endereço absoluto 0 não é de forma alguma especial no Redcode; de ​​fato, uma conseqüência interessante do design de "todos os endereços relativos" é que é realmente impossível para um programa Redcode encontrar seu próprio endereço absoluto no núcleo! )
Ilmari Karonen

2
Cara, eu sou muito grato a você. Eu nunca tinha ouvido falar sobre a Guerra do Núcleo.
seequ

32

C

#include <conio.h>  /* Computer-Operated Nuclear Installation Options */
int main () {
    clrscr();       /* Commence Launch (Remote Systems Console Request) */
    kbhit();        /* Keep Busy until hit */
}

Observe que este não é um código portátil. Funciona com o ZOG C no ART DS9000 . Acredito que o uso de armamentos não convencionais qualifica esse código para esse desafio. Se você estiver preocupado com o fato de o atraso necessário para que esse código entregue sua carga útil não se qualifique imediatamente , entre em contato conosco para obter orientações sobre ataques preventivos.

effect of launch

Na minha máquina doméstica, ele nem compila - eu não tenho os drivers e bibliotecas corretos. Ouvi dizer que existem implementações populares de C em que esse programa é compilado e executado com efeitos menos espetaculares, mas nunca tive coragem de tentar.


2
Melhor resposta, mãos para baixo!
Vector

3
+1. (0) 1; a princípio, pensei que @Gilles estivesse andando de um lado para o outro aqui ... mas depois chequei a página da ART Inc. ... e foi então que percebi o quanto eu estava sendo vasculhado!
precisa saber é o seguinte

30

Javascript

window.location.replace("http://pieisgood.org");

Basta navegar para um site ( delicioso ) diferente . :-)

Golfe (10 caracteres):

location=1

7
mmmm, sorvete: P
Volatilidade

28

C #

Se mata matando todos os processos, menos ele próprio.

foreach (Process p in Process.GetProcesses()) {
    string myexe = Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().CodeBase);
    if (p.ProcessName == myexe) continue;
    p.Kill();
}

Para apimentar um pouco, poderíamos Invocar em TerminateProcessvez de usarProcess.Kill


4
+1 por um método não convencional de matar todas as outras coisas, fazendo com que o seu próprio programa fique inativo com o sistema (ou pelo menos, suponho que essa seja a ideia?). Eu não tenho um tempo de execução C # nesta máquina, mas tenho que perguntar ... Na prática, isso acaba desativando o sistema ou apenas todos os processos em execução? (Eu pensei que os programas C # invocassem o intérprete, não contando com a existência de um processo JIT / CIL específico ...). Mais uma vez, um ótimo trabalho pensando 'fora da caixa' (har har) nesse caso :)
avanço

4
-1 (se eu tivesse o representante): a meta sobre trolling de código diz que respostas destrutivas não são permitidas devido ao risco de as pessoas experimentarem em suas próprias máquinas. Veja o segundo marcador na lista de restrições: codegolf.stackexchange.com/tags/code-trolling/info
Kevin - Restabelece Monica

Os processos do sistema não podem ser eliminados sem direitos de administrador, não são?
Sarge Borsch

3
@Kevin code-challenge e code-trolling são tags diferentes com regras diferentes.
osvein

3
@ user1981338: a pergunta originalmente tinha a tag de codificação, mas foi editada desde então. Agora que a tag sumiu, +1.
Kevin - Restabelece Monica

21

BASH - 12 caracteres

:(){ :|:&};:

Forkbomb clássico. Bloqueia o computador e força o usuário (ou administrador) a reiniciar.


5
Chato - mas ainda é o meu favorito. Eu uso isso para desligar sistemas ao vivo
s3lph

3
Começa a si próprio quantas vezes for possível, sem desistir.
Marinus

1
Mas ele vai começar tantos processos quanto possível até que as falhas no computador e, portanto, sai do programa
s3lph

14
+1 para adicionar smileys como :()e:|
ProgramFOX

1
@ Kartik: Sem o primeiro espaço, você receberá um erro de sintaxe. Você pode eliminar o segundo.
Dennis

20

Python (em laptops antigos)

Em laptops antigos com processadores de núcleo único e problemas de refrigeração:

while True:
    print("CPU Temperature rising")

Quando o laptop explodir (ou simplesmente desligar) após algumas horas, o programa não estará em execução.

(Para obter melhores resultados, enrole o laptop em um cobertor ou algo assim)



18

Apple 2 Basic

1 PRINT "HELLO"
2 POKE 2053,128
3 POKE 2054,58
4 GOTO 1

Ele substitui uma de suas instruções por um END.


Como isso funciona?
Johannes

5
@ Johnnes: no Applesoft BASIC, o programa é armazenado em uma forma tokenizada na memória. Como não há proteção do sistema operacional ou da memória, ele sempre é armazenado no mesmo local e não há nada para impedir a gravação nessa memória. Portanto, o primeiro POKEgrava um ENDtoken sobre o PRINTtoken e, em seguida, o segundo POKEgrava um terminador de comando sobre o primeiro ". Quando o GOTOé executado, ele acaba sendo executado em ENDvez de PRINTe o programa termina.
marinus

14

Na montagem, algo assim provavelmente funcionaria:

jmp 0

Lembro que compila isso realmente funcionou no DOS. Naquela época, ele reiniciava o computador.


tente ret; Na verdade, eu vou postar isso como resposta: P
Julien Lebot

RETF vai funcionar, eu já colocar isso em uma resposta :) Ele mata-lo por causa das obras de segmentação de memória forma (ou não funcionar, devo dizer) em sistemas operacionais modernos
chbaker0

»jmp 0« works with DOS, 'cause the first two byte of a code segment was CD 20, or 'INT 20' which is the code for exit programm. »push 0; ret« whould have the same effect.
Daniel

13

PHP

function quit(){unlink(__FILE__);posix_kill(getmypid(),15);}

Effects:
Deletes your crappy script file and then kills your program.
Throw one of these bad boys into some codebase to confuse people.


This is not valid PHP syntax. You try to define a function quit but the parameter list is code to run. I get a parse error when trying to run it.
Emil Vikström

@EmilVikström - Fixed now.
donutdan4114

12

C, 9 chars

Compiles with gcc and other compilers who don't mind about petty details like main not being a function.
Works on x86 platforms - the program immediately exits.

main=195;

195 is the opcode of the ret instruction.


4
Segfaults for me. main is located in .data because it's an int, not in .text, and .data is loaded into an non-executable page.
mniip

10

I always wanted to post this somewhere, I guess it fits here even though there's already an accepted answer.

int
main(int argc, char **argv)
{
        revoke(*argv);
}

This works on any post 4.3BSD system and others that implement revoke in the same way. Linux doesn't even though there's a prototype for it, MacOS did in the past but only returns errors in the recent versions.

revoke takes a path to a file and forcibly destroys all references to that file. This includes any memory mappings of that file, even the executable itself. Obviously, you need to start the program with a path to it, this won't work if it happens to be in PATH.

A variation of this that should work on most unix-like systems is:

#include <sys/mman.h>
#include <inttypes.h>
#include <unistd.h>

int
main(int argc, char **argv)
{
        intptr_t ps = getpagesize();
        munmap((void *)(((intptr_t)main)&~(ps - 1)), ps);
}

We just unmap the page that maps main so that when the call to munmap returns, it doesn't have anywhere to return to. There's a slight chance that the the function call to munmap is just on a page boundary and the return will succeed, so to be perfectly sure this works, we probably have to attempt to unmap two pages first.

And of course, a variation on the same theme:

#include <sys/mman.h>
#include <inttypes.h>
#include <unistd.h>

int
main(int argc, char **argv)
{
        intptr_t ps = getpagesize();
        munmap((void *)(((intptr_t)&ps)&~(ps - 1)), ps);
}

Just unmap the stack so that we don't have anywhere to return to. Same caveat as unmapping main - we might need to unmap two pages, except that we have to remember that the stack probably grows down (unless you're on PA-RISC or some other strange architecture like that).

Another way to pull the rug from under your own feet:

#include <sys/resource.h>

int
main(int argc, char **argv)
{
        setrlimit(RLIMIT_CPU, &((struct rlimit){ 0 }));
        while(1);
}

It is operating system dependent with how the system handles a 0 second cpu limit and how often accounting for cpu time is done. MacOS kills the process immediately, Linux requires the while loop, another system I tried didn't do anything even with a one second limit and a while loop, I didn't debug further.


9

sh

sudo kill -SEGV 1

Instant kernal panic on Linux. Destorys all processes, including itself

Cmd

pskill csrss.exe

Instant blue screen on Windows. Terminates the Client/Server Runtime SubSystem. Pskill must be installed.


Would killall init also work?
s3lph

1
@the_Seppi 1. Only a root-level user can send signals to init 2. Init will only cause a kernal panic if it receives signal 11 (segamentation fault)
MultiplyByZer0

OK thank you... Do you know what happens if you use sudo killall init
s3lph

1
@the_Seppi Yes, I do, I have tried it before. Init ignores it, because init can decide whether it receives the signal or not, so it completely ignores SIGKILL. unix.stackexchange.com/questions/7441/…
MultiplyByZer0

8

In C (Compatible Windows / Linux / (probably Unix / Freed BSD too)):

main;

Usage example:

Under Windows compile with:

echo main; > main.c && cl /Fe:main.exe main.c

And Linux:

echo "main;" > main.c && gcc -w -o main main.c

Assuming the compiler is installed and in the currenth PATH variable.

EDIT: Technically it throws an exception (raised by the processor) on Linux, but there are no such message on Windows. So this entry probably is not valid; however I think it's cool :P

EDIT: In x86 assembler (using NAsm/YAsm)

global s
s:
    ret

Compile with:

(Windows)

nasm -f win32 -o test.obj test.asm
LINK /OUT:test.exe /SUBSYSTEM:CONSOLE /ENTRY:s test.obj

(Linux)

nasm -f elf32 -o test.obj test.asm
ld -o test -e s test.obj

Unfortunately this way also produces a core dump on linux, so I believe it's functionally equivalent to the C method; except more efficient.


1
Wow! I just compiled this with -Wall -Wextra -std=c99 -pedantic and there are no warnings at all.
ldrumm

I actually wanted to put this, but decided against, because it would be duplicate of my solution to different problem (codegolf.stackexchange.com/a/8778/3103).
Konrad Borowski

7

Taking things literally in a silly way with Haskell:

import System.Exit

absolutely doThis = if True then doThis else undefined

unconventional doThat = do
  putStrLn "I could just do that"
  putStrLn "But I'm gonna print factorial of 100 first"
  putStrLn "There you go:"
  print $ fac 100
  doThat
  where fac n = foldl (*) 1 [1..n]

main = absolutely unconventional exitFailure

6

Python


Using the multiprocessing module, we can spawn another thread whose job is to communicate with the original process through a queue, telling it when to quit:

import multiprocessing
import time
queue = multiprocessing.Queue()

def second_thread():
    while True:
        queue.put('quit')
        time.sleep(0.1)

second_ps = multiprocessing.Process(target = second_thread)
second_ps.start()

while True:
    msg = queue.get()
    if msg == 'quit':
        break
    time.sleep(0.1)

second_ps.join()

6

ANSI C

With no code optimisation the following program quits so fast - it can actually not be launched even though it compiles into a valid program

#include<stdlib.h>
#include<stdio.h>

char tooLong[0x7CFFFFFF];

void main()
{
    printf("never executed.");
}

this may be system dependent - comment if you can launch it.


"Quitting by throwing exceptions won't be accepted any more!" and this includes out-of-memory errors
John Dvorak

2
@JanDvorak There are differences between errors and exceptions.
syb0rg

I believe the rule encompasses both, however. Can the asker clarify? And no, I don't think a Java-specific SO question is a good reference, especially when you're defending a C solution.
John Dvorak

9
The program neither throws an error nor does it cause an error within the program. The system can not handle it.
Johannes

6

Okay. If simply calling System.exit isn't permitted in Java, how about calling it via reflection from another thread?

import java.lang.reflect.*;

public class Quit {
    public static void main(String[] args) throws Exception {
        final Method exit = System.class.getMethod("exit", new Class<?>[]{ int.class });
        new Thread(new Runnable() {
            @Override public void run() {
                try {
                    System.out.println("calling... " + exit);
                    exit.invoke(null, new Object[] { 0 });
                } catch (Exception e) { e.printStackTrace(); }
            }
        }).start();
        for (int i = 1; ; ++i) {
            System.out.println("counting... " + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) { break; }
        }
    }
}

Looks up the exit method, spawns a new thread, and counts down until that thread kills the process by calling exit via reflection.


Counts down? Looks like it counts up to me.
Justin

1
Er, yes, good point. I should have said counts up, or just said that it counts until it exits.
David Conrad

6

x86 machine language, 1 character

You can usually make a workable executable out of one RET instruction

\xC3 

4
How is that unconventional?!
aditsu

6

C (Linux)

Suicide version

Sends a SIGKILL to itself.

#include <stdio.h>
#include <signal.h>
#include <unistd.h>

int main() 
{
    kill(getpid(),SIGKILL);

    printf("Not killed");
}

"Tu quoque mi fili" version

Forks, then the son kills the father.

#include <stdio.h>
#include <signal.h>
#include <unistd.h>

int main() 
{
    if (!fork())
        kill(getppid(), SIGKILL);

    printf("Not killed");
}

5

Perl

...

That is all.


Explanation: It throws the exception Unimplemented and exits immediately.


Added explanation.
PhiNotPi

3
I don't know, exceptions are boring.

6
Invalif due to challenge adaption: no exceptions
s3lph

I just haven't gotten around to writing another answer yet.
PhiNotPi

5

Batch & Debug

An oldie but goodie, works on DOS machines for an instant system reboot. Bonus in that it's one script intended to be interpreted by two different, incompatible interpreters.

reset.bat

goto start

rcs
ffff
rip
0000
g
:start
debug < reset.bat

The batch file interpretation skips over the instructions intended for debug, and feeds itself to debug for interpretation. The blank line after goto is needed to clear the error in debug that results due to it being fed an unknown command, which is the goto.


1
I used to do the same thing as dec ax; push ax; push bx; retf
moopet

3

Java

The below code is not tested and will only work on some platforms.

public class Quitter
{
       public static void main ( String [ ] args )
       {
             Process process = Runtime . getRuntime ( ) . exec ( "reboot" ) ;
       }
}

45
What is with that horrendous whitespace
Doorknob

7
On UNIX it would have to be sudo reboot, on Windows shutdown /r /t 0
s3lph

2
On UNIX you just have to execute that program with sudo, not the command that you want to execute.
Johannes Kuhn

1
@the_Seppi,@JohannesKuhn like I said it only works on some platforms and I was assuming you were going to invoke it like sudo java Quitter.
emory

3
-1 NOT ENOUGH WHITESPACE!!!
vaxquis

3

Powershell

get-process | stop-process -force

Save this as 'lastresort.ps1' and it should do the trick.

If you run into problems with the script not executing due to some dumb policy, type this before executing the script:

Set-ExecutionPolicy Unrestricted

Works just fine from the command line without saving as a script also.
Iszi

3

TI-Basic 84

:;;::;banana\\sEnd\;:;1:If X=X-1 :eat banana juice and lol;;;::::;:thought you could EAT THINGS XD /// CRAZIEST ANSWER YET!!!

3

Python (a one line alternative to the OP)

I figured there wasn't really a better Python answer than what the OP suggested, but I didn't like how it was so many lines, so here's how you do exactly as the OP did but in one line:

exec(''.join([ chr(x) for x in [35, 33, 47, 117, 115, 114, 47, 98, 105, 110, 47, 101, 110, 118, 32, 112, 121, 116, 104, 111, 110, 10, 35, 32, 117, 110, 105, 120, 32, 111, 110, 108, 121, 44, 32, 109, 105, 103, 104, 116, 32, 119, 111, 114, 107, 32, 111, 110, 32, 119, 105, 110, 100, 111, 119, 115, 10, 35, 32, 110, 111, 116, 101, 58, 32, 107, 105, 108, 108, 115, 32, 65, 76, 76, 32, 82, 85, 78, 78, 73, 78, 71, 32, 112, 121, 116, 104, 111, 110, 32, 112, 114, 111, 99, 101, 115, 115, 101, 115, 46, 32, 66, 101, 32, 99, 97, 114, 101, 102, 117, 108, 32, 47, 33, 92, 10, 100, 101, 102, 32, 101, 120, 105, 116, 40, 41, 58, 10, 32, 32, 32, 32, 105, 109, 112, 111, 114, 116, 32, 111, 115, 10, 32, 32, 32, 32, 111, 115, 46, 115, 121, 115, 116, 101, 109, 40, 34, 107, 105, 108, 108, 97, 108, 108, 32, 112, 121, 116, 104, 111, 110, 51, 34, 41, 10, 32, 32, 32, 32, 35, 32, 87, 105, 110, 100, 111, 119, 115, 32, 97, 100, 100, 111, 110, 10, 32, 32, 32, 32, 111, 115, 46, 115, 121, 115, 116, 101, 109, 40, 34, 116, 97, 115, 107, 107, 105, 108, 108, 32, 47, 105, 109, 32, 112, 121, 116, 104, 111, 110, 46, 101, 120, 101, 32, 47, 102, 34, 41, 32, 35, 32, 111, 114, 32, 119, 104, 97, 116, 101, 118, 101, 114, 32, 102, 105, 108, 101, 110, 97, 109, 101, 32, 112, 121, 116, 104, 111, 110, 64, 119, 105, 110, 100, 111, 119, 115, 32, 104, 97, 115, 10, 101, 120, 105, 116, 40, 41, 10] ]))

You can make this a function and it will do the job for you.


3

My own idea, not participating

TIGCC (for Texas Instrumens TI-89, TI-89 Titanium, TI-92+, TI-V200)

void main(void) {
    unlink("quit");
    asm("trap #2");
}

TI-Basic for the same calculators

quit()
:© lines starting with © are comments
:Prgm
:©DelVar quit
:Exec "4E424E750000"
:EndPrgm

What the program does: First it deletes itself out of the RAM. (Don't put it to the ROM or it won't work...) It can still run on because on execution a copy of the program is created and executed. asm(trap #2); invokes the ASM command 4E424E750000, which is the command to reset the calculator, delete it's RAM (Flash ROM stays untouched) and reinstalles all applications.

EDIT: Just tested the Basic version. It can't delete itself...


3

MS-DOS .com format

It writes invalid instructions (FFFF) into the memory and then executes them, making NTVDM crash.

Hexadecimal

B8 FF FF A3 06 01

Debug.exe assembly language

MOV AX, FFFF
MOV [0106],AX
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.